python线程类实例参数传递_Python多线程
threading模块
线程简述
线程(轻量级进程)与进程类似,不过它们是在同⼀个进程下执⾏的,并共享相同的上下⽂。可以将它们认为是在⼀个主进程或"主线程"中并⾏运⾏的⼀些"迷你进程"。
线程包括开始、执⾏顺序和结束三部分。它有⼀个指令指针,⽤于记录运⾏的上下⽂。它其他线程运⾏时,它可以被抢占(中断)和临时挂起(睡眠/加锁)---这种做法叫做让步(yielding)。
多线程的创建
使⽤Thread类,可以有很多⽅法来创建线程,其中常⽤的有:
创建Thread的⽰例,传给它⼀个函数;
派⽣Thread的⼦类,重新run⽅法,并创建⼦类的实例。
⽰例1:创建Thread的实例,传给它⼀个函数
from threading import Thread
import time
def test():
print("---hello-world---")
time.sleep(1)
for i in range(5):
#创建线程,线程执⾏的任务是target指定的函数,如果函数需要传⼊参数,则可以指定args=(),或者kwargs={}
t = Thread(target=test)
t.start()
运⾏结果:
---hello-world---
---hello-world---
---hello-world---
---hello-world---
---hello-world---
⽰例2:使⽤Thread⼦类创建线程
import threading
import time
# ⾃定义类继承threading类
class myThread(threading.Thread):
# 重新run⽅法
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm " + self.name+' @ '+str(i)
print(msg)
if __name__ == "__main__":
# 创建线程
t = myThread()
t.start()
运⾏结果:
I'm Thread-1 @ 0
I'm Thread-1 @ 1
I'm Thread-1 @ 2
python的threading.Thread类有⼀个run⽅法,⽤于定义线程的功能函数,可以在⾃⼰的线程类中覆盖该⽅法。⽽创建⾃⼰的线程实例后,通过Thread类的start⽅法,可以启动该线程,交给python虚拟机进⾏调度,当该线程获得执⾏的机会时,就会调⽤run⽅法执⾏线程。
多线程共享全局变量
在⼀个进程中,多个线程之间是共享全局变量的,即⼀个线程修改了全局变量,另外⼀个线程在此之后获取的这个全局变量是被修改后的。⽐如下⾯例⼦:
from threading import Thread
import time
num = 100
def thread1():
global num
for i in range(3):
num += 1
print("I'am Thread1 ." + " my num is "+str(num))
def thread2():
print("I'am Thread2. " +" my num is "+ str(num))
t1 = Thread(target=thread1)
t1.start()
# 让程序睡眠1秒钟,确保线程1执⾏完毕。
time.sleep(1)
t2 = Thread(target=thread2)
t2.start()
运⾏结果:
I'am Thread1. my num is 103
I'am Thread2. my num is 103
线程关于全局变量注意点
在⼀个进程内的所有线程共享全局变量,能够在不适⽤其他⽅式的前提下完成多线程之间的数据共享(这点要⽐多进程要好)
⼀个进程中的各个线程与主线程共享同⼀⽚数据空间。因此与进程相⽐,线程之间的信息共享和通信更加容易。在⼀个程序中,线程的执⾏是:每个线程运⾏⼀⼩会,然后让步给其他线程(再次排队等待更多的CPU时间)。在整个进程的执⾏过程中,每个线程执⾏它⾃⼰特定的任务,在必要时和其他线程进⾏通信。
当然,这种共享是有风险的。如果两个或多个线程访问同⼀⽚数据,由于数据的访问顺序不同可能导致结果不⼀致。这种情况叫竞态条件。⾟运的是,⼤多数线程库都有⼀些同步源语,以允许线程管理器控制执⾏和访问。
同步和互斥锁
同步
⼀般在多线程代码中,总有⼀些函数或者代码块不希望被多个线程同时执⾏,如果有两个线程运⾏的顺序发⽣变化,就有可能造成代码的执⾏轨迹或⾏为不同,产⽣不⼀样的数据。这时候就需要使⽤同步了。
同步可以理解为协同步调,按预定的先后次序进⾏运⾏,⽐如你先说,我再讲。
⽰例1:多个线程对全局变量修改的bug
from threading import Thread
import time
num = 0
def work1():
global num
for i in range(1000000):
num += 1
print("-work1-num:%d"%num)
def work2():
global num
for i in range(1000000):
num += 1
print("-work2-num:%d"%num)
t1 = Thread(target=work1)
t1.start()
#time.sleep(3)
t2 = Thread(target=work2)
t2.start()
运⾏结果:
-work1-num:1105962python虚拟机
-work2-num:1150358
这个程序是两个线程同时对全局变量num进⾏相加操作,但是因为多线程中线程的执⾏顺序是不同的,因此出现最后相加结果不是2000000的结果。
⽰例2:避免全局变量被修改的⽅法
避免上⾯的情况可以有很多种⽅法,第⼀种是将上⾯time.sleep(3)的注释去掉,就是在3秒内让线程1执⾏,3s内执⾏完毕再执⾏线程2对num变量进⾏⾃增。(不过这种⽅法跟单线程没区别,也就没有意义去创建多线程了...)
第⼆种就是使⽤轮询,代码⽰例如下:
from threading import Thread
import time
num = 0
item = 1
def work1():
global num
global item
if item == 1:
for i in range(1000000):
num += 1
item = 0
print("-work1-num:%d"%num)
def work2():
global num
while True:# 轮询,⼀直查看条件是否满⾜,线程2⼀直在执⾏...
if item != 1:
for i in range(1000000):
num += 1
break
print("-work2-num:%d"%num)
t1 = Thread(target=work1)
t1.start()
#time.sleep(3)
t2 = Thread(target=work2)
t2.start()
运⾏结果:
-work1-num:1000000
-work2-num:200000
这次结果就正确的了,不过这种⽅法效率也不⾼。第三种⽅法就是锁了。
互斥锁
当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互斥锁。
互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程
释放资源,将资源的状态变成“⾮锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性。
threading模块定义了Lock类,可以很⽅便地进⾏锁定。
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([blocking])
#释放
其中,锁定⽅法acquire可以有⼀个blocking参数。
如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为⽌(如果没有指定,那么默认为True)
如果设定blocking为False,则当前线程不会堵
⽰例:
from threading import Thread,Lock
import time
num = 0
def work1():
global num
# 上锁
mutex.acquire()
for i in range(1000000):
num += 1
# 解锁
print("-work1-num:%d"%num)
def work2():
global num
mutex.acquire()
for i in range(1000000):
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论