资料例子来自尚学堂
-
python创建线程的两种方式
- 方法实现
from threading import Thread from time import sleep, time import threading def fun(name): print(threading.currentThread()) print(f"Threading:{name} start") sleep(3) # 测试线程并发 print(f"Threading:{name} end") if __name__ == "__main__": t1 = Thread(target=func, args=("t1",)) # 创建线程对象,target指定执行func t2 = Thread(target=func, args=("t2",)) # 创建线程对象,target指定执行func start = time() t1.start() # 线程对象可以调用start,启动该线程 t2.start() t1.join() # join,等待t1执行完毕,注意不能加在t1.start()后面,会造成串行 t2.join() # join,等待t2执行完毕
- 类实现
from threading import Thread from time import sleep # 类实现-线程 class MyThread(Thread): # 继承Thread对象 def __init__(self, name): Thread.__init__(self) self.name = name def run(self): # 此处需是run(),内容及方法实现里的func() pritn(f"Threading:{self.name} start") sleep(3) if __name__ == "__main__": t1 = MyThread("t1") t2 = MyThread("t2") t1.start() t2.start()
-
守护线程(Daemon)
- 子线程设置为守护线程,则主线程一旦结束,其子线程同时结束
- 设置方法:t1.setDaemon(True)
-
线程锁
- 基本使用
from threading import Thread from threading import Lock # add-添加锁 def func1(name): lock.acquire() # add-获得线程锁,相当于要操作count变量,先拿到权限 global count for i in range(100000): count += 1 lock.release() # add-释放线程锁,处理完后释放权限 if __name__=="__main__": count = 0 t_list = [] lock = Lock() # add-添加线程锁 for i in range(10): t = Thread(target=func1, args=(f't{i+1}',)) t.start() t_list.append(t) for t in t_list: t.join() print(count) # 不添加锁,则结果为939307,且随机
- 理解锁
- Python的解释器环境中(Cpython),多线程执行是假象,同一时间执行的线程只有一个,这是python开发的设计缺陷?(# TODO)
- GIL:Global Interpreter Lock 全局解释器锁控制对python虚拟机的访问
- 如上例,func1中如果range里面是个小数值,比如100,那么GIL可以保证结果不出错,但是一旦数值达到100000,那么GIL也无法保证不出错;因为GIL要确保多线程的假象,即每个线程不能持续执行太久;此时需要加Lock对象;
- 使用锁🔐的情况
- 必须使用同一把锁
- 使用锁,程序就成串行,因此要适当使用;查询数据没必要加锁
- 使用with效果和加锁一样(with lock: xxx == lock.acquire() + xxx + lock.release())
- 多线程中错误使用🔐,可能导致随机数据损坏或者其他异常,成为竞争条件;最好只在临界区(操作临界资源那部分代码)使用🔐;
- 死锁:情况1,交叉持有锁
from threading import Thread, Lock from time import sleep def func1(): # 炒西蓝花 lock1.acquire() print('func1拿到菜刀-切菜') sleep(20) lock2.acquire() # ==>死锁原因:持有lock1,等待lock2释放 print('func1拿到锅-热锅') lock2.release() print('func1释放锅-完成') lock1.release() print('func1释放菜刀-完成') def func2(): # 炒红烧肉 lock2.acquire() print('func2拿到锅-热锅') lock1.acquire() # ==>死锁原因:持有lock2,等待lock1释放,陷入死循环 print('func2拿到菜刀-切菜') lock1.release() print('func2释放菜刀-完成') lock2.release() print('func2释放锅-完成') if __name__ == "__main__": lock1 = Lock() # 菜刀 lock2 = Lock() # 锅 t1 = Thread(target==func1) t2 = Thread(target==func2) t1.start() t2.start()
- 死锁:情况2,循环使用锁
from threading import Lock # from threading import RLock def func1(): lock.acquire() print("func1拿到锁") func2() lock.release() print("func1释放锁") def func2(): lock.acquire() print("func2拿到锁") ... lock.release() print("func2释放锁") def func3(): # 死锁原因:调用func3->func1拿到🔐->func1调用func2->func2也要使用🔐,需要等func1执行完释放🔐;但是func1持有🔐且在等待func2执行完,死锁; func1() func2() if __name__=="__main__": lock = Lock() # 使用同步锁、互斥锁 # lock = RLock() # 解决方法:使用递归锁RLock替代Lock解决嵌套死锁 func3()
-
信号量(Semaphore)
- 用于指定同时进行的线程个数
from time import sleep from threading import Thread from threading import BoundedSemaphore def anj(num): lock.acquire() # 获取信号量🔐,成功获取的才可执行;执行完毕释放信号量🔐供其他线程使用; print(f"第{num}个人完成安检") sleep(3) lock.release() if __name__=="__main__": lock = BoundedSemaphore(5) # 如果没有信号量,10个线程同时执行;引入信号量,通过参数控制同时执行线程数; for i in range(10): t = Thread(target=anj, args=(f"{i+1}", )) t.start()
-
事件(Events)
- 通过Event()控制某个事件的进行;
from threading import Thread, Event from time import sleep from random import randint state = 0 def car(): num = 0 while True: if event.is_set(): print("车已到站,请上车") sleep(1) num += 1 if num % 5 == 0: event.clear() # 人满车走,event复位为False else: print("车开走了...") sleep(6) event.set() # set True,本例子中event控制的事件即车是否到站,此处到站 def person(): while True: if event.is_set(): # 车是否到了,检查内置bool是否为True print("上车!!!") sleep(1) else: print("等待上车...") # 此处是event与普通bool控制变量的不同之处,event可以阻塞当前线程, # 普通bool控制变量会持续打印 "等待上车...";一旦event.set()调用,该线程被唤醒执行; event.wait() if __name__ == "__main__": event = Event() # 设置事件对象,内置bool类型标志位,控制事件行进开关,默认False t1 = Thread(target=car) t2 = Thread(target=person) t1.start() t2.start()
- 例子2:自动闸门,刷卡进入,门打开超过3s自动关闭;
from time import sleep from threading import Thread, Event from random import randint def door(): global state while True: if event.is_set(): print("门开着,可以通行") sleep(1) else: state = 0 print("门关着,请刷卡") event.wait() # 阻塞,等待person内设置的event.set()唤醒 sleep(1) state += 1 if state > 3: print("超过3s,门自动关闭") event.clear() def person(): global state num = 1 while True: if event.is_set(): print(f"门开着,{num}号进入") else: print(f"门关着,{num}号刷卡进入") event.set() # 唤醒所有等待事件的线程(此处事件即门开启) state = 0 num += 1 sleep(randint(1,10)) if __name__ == "__main__": state = 0 event = Event() t1, t2 = Thread(target=door), Thread(target=person) t1.start() t2.start()