python-多线程
通过用threading模块的Thread类创建线程
创建线程常用的两个参数:target和args
target表示线程要运行的函数名,不需要加括号'()'
args表示target的函数所需的入参,类型时元组'(xxx,)',函数无入参时该参数可省略
import time import threading begin = time.time() print(begin) def foo(n): print("foo()",n) time.sleep(1)#睡一秒 print("end foo() ->",time.time()) def bar(n): print("bar()",n) time.sleep(2)#睡两秒 print("end bar() ->",time.time()) # 创建两个线程 t1 = threading.Thread(target=foo,args=(1,)) t2 = threading.Thread(target=bar,args=(2,)) # 启动线程 t1.start() t2.start() end = time.time() print(end-begin)
线程阻塞:join --> 主线程阻塞,等子线程执行完主线程才可以往下走
线程守护:setDaemon --> 当设为True时,该子线程随主线程结束而结束
当前线程信息:threading.current_thread()
当前线程数:threading.active_count()
import threading from time import ctime,sleep def music(func):
# 打印当前线程的信息 print(threading.current_thread()) for i in range(2): print ("Begin listening to %s. %s" %(func,ctime())) sleep(2) print("end listening %s"%ctime()) def move(func):
# 打印当前线程的信息 print(threading.current_thread()) for i in range(2): print ("Begin watching at the %s! %s" %(func,ctime())) sleep(3) print('end watching %s'%ctime()) threads = [] t1 = threading.Thread(target=music,args=('七里香',)) threads.append(t1) t2 = threading.Thread(target=move,args=('阿甘正传',)) threads.append(t2) if __name__ == '__main__':
# 开启进程守护
t1.setDaemon(True) for t in threads: # t.setDaemon(True) t.start()
# 进程阻塞
t2.join()
# t1.join()
# 打印当前线程的信息(主线程) print(threading.current_thread())
# 还在运行线程数 print(threading.active_count()) print ("all over %s" %ctime())
通过继承threading.Thread来创建线程
import time import threading #创建一个类继承threading.Thread class MyThread(threading.Thread): #只需要重写run函数即可 def run(self): time.sleep(1) print(self.name) if __name__ == '__main__': thrds = [] for a in range(1,6): thrds.append(MyThread()) for t in thrds: t.start() for t in thrds: t.join()
当多个线程都在同时操作同一个共享资源,可能会造成了资源破坏,可以创建同步锁来解决这个问题
创建同步锁:r = threading.Lock()
开启锁:r.acquire()
释放锁:r.release()
不加这锁的话结果会不一样
加完锁程序变成串行了,每个都会开锁再解锁,一个一个运行
import time import threading # 创建锁 r = threading.Lock() def addNum(): global num #在每个线程中都获取这个全局变量 # 开启锁 r.acquire() temp=num print('--get num:',num ) time.sleep(0.0001) num =temp-1 #操作 # 释放锁 r.release() num = 100 #设定一个共享变量 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待所有线程执行完毕 t.join() print('final num:', num )
同步锁有时候会造成死锁
import threading,time #死锁 class myThread(threading.Thread): def doA(self): lockA.acquire() print(self.name,"gotlockA",time.ctime()) time.sleep(3) lockB.acquire() print(self.name,"gotlockB",time.ctime()) lockB.release() lockA.release() def doB(self): lockB.acquire() print(self.name,"gotlockB",time.ctime()) time.sleep(2) lockA.acquire() print(self.name,"gotlockA",time.ctime()) lockA.release() lockB.release() def run(self): self.doA() self.doB() if __name__=="__main__": #创建两个同步锁 lockA=threading.Lock() lockB=threading.Lock() threads=[] for i in range(5): threads.append(myThread()) for t in threads: t.start() for t in threads: t.join()
举个为啥用同步锁会造成死锁的例子
得用递归锁解决
#帐户类 class Account: def __init__(self,id,money): self.id = id self.balance = money self.r = threading.RLock() #支取 def draw(self,money): self.r.acquire() self.balance -= money self.r.release() #存入 def deposit(self,money): self.r.acquire() self.balance += money self.r.release() #既取又存 def test(self,money): #如果不用递归锁这里就可能会出现死锁 self.r.acquire() self.balance -= money self.deposit(money) self.r.release() #转帐 def transfer(a,b,money): a.draw(money) b.deposit(money) #初始化帐户 a = Account(1001,1000) b = Account(1002,2000) #初始化两个转帐线程 t1 = threading.Thread(target=transfer,args=(a,b,100)) t2 = threading.Thread(target=transfer,args=(b,a,200)) #开启两个线程 t1.start() t2.start() #开启线程阻塞 t1.join() t2.join()
递归锁:threading.RLock。可重入锁,就是同一把锁可以锁多次,锁几次就得释放几次,不然其他线程在这个地方都会被锁住。
RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
上面的死锁用递归锁实现
import threading,time #递归锁(可重用):threading.RLock class myThread(threading.Thread): def doA(self): lock.acquire() print(self.name,"gotlockA",time.ctime()) time.sleep(3) lock.acquire() print(self.name,"gotlockB",time.ctime()) lock.release() lock.release() def doB(self): lock.acquire() print(self.name,"gotlockB",time.ctime()) time.sleep(2) lock.acquire() print(self.name,"gotlockA",time.ctime()) lock.release() lock.release() def run(self): self.doA() self.doB() if __name__=="__main__": #创建递归锁 lock=threading.RLock() threads=[] for i in range(5): threads.append(myThread()) for t in threads: t.start() for t in threads: t.join()
有一类线程需要满足条件之后才能够继续执行,
Python提供了threading.Condition 对象用于条件变量线程的支持,
它除了能提供RLock()或Lock()的方法外,
还提供了 wait()、notify()、notifyAll()方法。
lock_con=threading.Condition([Lock/Rlock]):
锁是可选选项,不传人锁,对象自动创建一个RLock()。
wait()方法释放锁并等待notify()方法的激活
notify()方法不释放锁,用来激活wait()
import time import threading import random #生产者 class Product(threading.Thread): def run(self): global L while True: con_lock.acquire() val = random.randint(0,100) L.append(val) print('生产者:',self.name,'Append',str(L)) con_lock.notify() con_lock.release() time.sleep(3) #消费者 class Consume(threading.Thread): def run(self): global L while True: con_lock.acquire() if len(L) == 0: con_lock.wait() print('OK') val = L.pop(0) print('消费者:',self.name,'Delete',val,' Left',str(L)) con_lock.release() time.sleep(0.5) if __name__ == '__main__': L = [] con_lock = threading.Condition() thrs = [] for a in range(6): thrs.append(Product()) thrs.append(Consume()) for t in thrs: t.start() for t in thrs: t.join()
信号量:用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
import time import threading #信号量:Semaphore/BoundedSemaphore(类似于停车位的概念) class MyThread(threading.Thread): def run(self): semaphore.acquire() print(self.name) time.sleep(3) semaphore.release() if __name__=="__main__": semaphore = threading.BoundedSemaphore(5) thrs = [] for a in range(100): thrs.append(MyThread()) for t in thrs: t.start() for t in thrs: t.join()
多线程利器:queue,即队列
队列的常用操作
#首先 导入queue模块 import queue #创建一个'队列'对象 q = queue.Queue(maxsize = 10) # Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。 # 可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。 #将一个值放入队列中 q.put(10) #调用队列对象的put()方法在队尾插入一个项目。 # put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。 # 如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。 #将一个值从队列中取出 q.get() #调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。 # 如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。 ''' Python Queue模块有三种队列及构造函数: 1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize) 2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize) 3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize) ''' #此包中的常用方法: q = queue.Queue() #创建一个队列 q.qsize() # 返回队列的大小 q.empty() # 如果队列为空,返回True,反之False q.full() # 如果队列满了,返回True,反之False q.full # 与 maxsize 大小对应 q.get(block=False,timeout=3) # block和timeout都不是必填项,block默认True,timeout等待时间默认None q.get_nowait() # 相当q.get(False) q.put('item') # 非阻塞时,写入队列,timeout等待时间 q.put_nowait('item') # 相当于 --> q.put('item', False) q.task_done() # 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 q.join() # 实际上意味着等到队列为空,再执行别的操作
简单的例子
import threading,queue,time,random class Production(threading.Thread): def run(self): while True: r = random.randint(0,100) q.put(r) print("生产出来%s号包子"%r) time.sleep(1) class Proces(threading.Thread): def run(self): while True: ret = q.get() print("吃掉%s号包子" % ret) if __name__=="__main__": q = queue.Queue(10) thrds = [Production(),Production(),Production(),Proces()] for t in thrds: t.start()