python提供了几个用于多线程编程的模块,thread,threading,Queue等。其中thread模块提供了基本的线程和锁的支持,threading提供了更高级别的功能,Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
因为thread有下面几个缺点,所以建议使用threading模块:
1.threading更为先进
2.thread模块的同步原语很少
3.当主线程结束时所有的线程都会被强制结束,没有警告也没有正常的清理工作
4.不支持守护线程,也就是不能设置线程为daemon
所以下面重点介绍threading的三种创建进程的方法。
- 创建一个Thread实例,传给他一个函数
import threading
from time import ctime,sleep
loops=[4,2]
def loop(nloop,nsecond):
print('start loop ',nloop,'at: ',ctime())
sleep(nsecond)
print('loop ',nloop,' done at: ',ctime())
def main():
print('starting at: ',ctime())
threads=[]
nloops=range(len(loops))
for i in nloops:
#'target','args' are must
threads.append(threading.Thread(target=loop,args=(i,loops[i])))
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at: ',ctime())
if __name__=='__main__':
main()
- 创建一个Thread实例,传给他一个可调用的类对象
其中这个类对象要是可调用的对象,也就是实现了call函数的类
import threading
from time import ctime,sleep
loops=[4,2]
class ThreadFunc():
def __init__(self,func,args,name=''):
self.name=name
self.func=func
self.args=args
def __call__(self):
self.func(*self.args)
def loop(nloop,nsecond):
print('start loop ',nloop,'at: ',ctime())
sleep(nsecond)
print('loop ',nloop,' done at: ',ctime())
def main():
print('starting at: ',ctime())
threads=[]
nloops=range(len(loops))
for i in nloops:
threads.append(threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)))
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at: ',ctime())
if __name__=='__main__':
main()
3.从Thread派生出一个子类,创建一个子类的实例
(1)这个子类的构造器要先调用基类的构造器;(2)子类要实现run函数
import threading
from time import ctime,sleep
class MyThread(threading.Thread):
def __init__(self,func,args,name=""):
threading.Thread.__init__(self)
self.name=name
self.func=func
self.args=args
self.res=0
def getResult(self):
return self.res
def run(self):
print('starting ',self.name,'at: ',ctime())
self.res=self.func(*self.args)
print(self.name,' finished at: ',ctime())
def Fib(n):
sleep(0.005)
if n<2:return 1
return Fib(n-1)+Fib(n-2)
def Fac(n):
sleep(0.1)
if n<2:return 1
return n*Fac(n-1)
def Sum(n):
sleep(0.1)
if n<1:return 0
return n+Sum(n-1)
func=[Fib,Fac,Sum]
n=12
def main():
threads=[]
nfunc=range(len(func))
print('***MULTIPE THREAD')
for i in nfunc:
threads.append(MyThread(func[i],(n,),func[i].__name__))
for i in nfunc:
threads[i].start()
for i in nfunc:
threads[i].join()
print(threads[i].getResult())
print('all DONE')
if __name__=='__main__':
main()
下面是queue模块,它提供了三个类,Queue,LifoQueue,PriorityQueue
具体详见:Python queue模块
其中的两个方法:
put(item,block=0)如果block不等于0,函数会一直阻塞下去到队列中有空间为止
get(block=0)block的作用同put
import threading
from time import ctime,sleep
from queue import Queue
from random import randint
class MyThread(threading.Thread):
def __init__(self,func,args,name=""):
threading.Thread.__init__(self)
self.name=name
self.func=func
self.args=args
self.res=0
def getResult(self):
return self.res
def run(self):
print('starting ',self.name,'at: ',ctime())
self.res=self.func(*self.args)
print(self.name,' finished at: ',ctime())
def writeQ(queue):
print('producting object for Q...')
queue.put('xxx',1)
print('size now',queue.qsize())
def readQ(queue):
val=queue.get(1)
print('consumed object from Q...size now',queue.qsize())
def writer(queue,loops):
for i in range(loops):
writeQ(queue)
sleep(randint(1,3))
def reader(queue,loops):
for i in range(loops):
readQ(queue)
sleep(randint(2,5))
func=[writer,reader]
n=12
def main():
threads=[]
nfunc=range(len(func))
q=Queue(32)
nloops=randint(2,5)
print('***MULTIPE THREAD')
for i in nfunc:
threads.append(MyThread(func[i],(q,nloops),func[i].__name__))
for i in nfunc:
threads[i].start()
for i in nfunc:
threads[i].join()
print('all DONE')
if __name__=='__main__':
main()
然后简单的介绍一下threading中的另外三个类Lock,Condition,Event。
Lock:
其实很简单就是加锁控制资源访问
import threading
import time
class MyThread(threading.Thread):
def run(self):
global num
time.sleep(1)
if mutex.acquire(1):
num = num+1
msg = self.name+' set num to '+str(num)
print(msg)
mutex.release()
num = 0
mutex = threading.Lock()
def test():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
test()
Evevt类:
多个线程等待某个事件发生,发生后,所有的线程被激活
Event对象实现了简单的线程通信机制,它提供了设置信号,清楚信号,等待等用于实现线程间的通信。
1 设置信号
使用Event的set()方法可以设置Event对象内部的信号标志为真。Event对象提供了isSet()方法来判断其内部信号标志的状态。当使用event对象的set()方法后,isSet()方法返回真
2 清除信号
使用Event对象的clear()方法可以清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假
3 等待
Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,则wait方法一直等待到其为真时才返回。
import threading
class mythread(threading.Thread):
def __init__(self,threadname):
threading.Thread.__init__(self,name=threadname)
def run(self):
global event
if event.isSet():
event.clear()
event.wait()
print(self.getName())
else:
print(self.getName())
event.set()
event=threading.Event()
event.set()
t1=[]
for i in range(10):
t=mythread(str(i))
t1.append(t)
for i in t1:
i.start()
Condition类:
使用Condition对象可以在某些事件触发或者达到特定的条件后才处理数据,Condition除了具有Lock对象的acquire方法和release方法外,还有wait方法、notify方法、notifyAll方法等用于条件处理。
threading.Condition([lock]):创建一个condition,支持从外界引用一个Lock对象(适用于多个condtion共用一个Lock的情况),默认是创建一个新的Lock对象。
acquire()/release():获得/释放 Lock
wait([timeout]):线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。调用wait()会释放Lock,直至该线程被Notify()、NotifyAll()或者超时线程又重新获得Lock.
notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。
notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程(这个一般用得少)
import threading
import time
condition = threading.Condition()
products = 0
class Producer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global condition, products
while True:
if condition.acquire():
if products < 10:
products += 1;
print("Producer(%s):deliver one, now products:%s" %(self.name, products))
condition.notify()
else:
print("Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products))
condition.wait();
condition.release()
time.sleep(2)
class Consumer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global condition, products
while True:
if condition.acquire():
if products > 1:
products -= 1
print("Consumer(%s):consume one, now products:%s" %(self.name, products))
condition.notify()
else:
print("Consumer(%s):only 1, stop consume, products:%s" %(self.name, products))
condition.wait();
condition.release()
time.sleep(2)
if __name__ == "__main__":
for p in range(0, 2):
p = Producer()
p.start()
for c in range(0, 10):
c = Consumer()
c.start()