python theading线程开发与加锁、信号量、事件等详解
线程有2种调用方式,如下:
直接调用
1 import threading 2 import time 3
4 def sayhi(num): #定义每个线程要运行的函数 6 print("running on number:%s" %num) 8 time.sleep(3) 9 10 if __name__ == '__main__': 12 t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 13 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 15 t1.start() #启动线程 16 t2.start() #启动另一个线程 18 print(t1.getName()) #获取线程名 19 print(t2.getName())
继承式调用
import threading import time
class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self):#定义每个线程要运行的函数 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
同步锁(py2版本) *注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁
1 import time 2 import threading 3 4 5 def addNum(): 6 global num # 在每个线程中都获取这个全局变量 7 print('--get num: %s' %num) 8 time.sleep(1) 9 lock.acquire() # 修改数据前加锁 10 num -= 1 # 对此公共变量进行-1操作 11 lock.release() # 修改后释放 12 13 num = 100 # 设定一个共享变量 14 thread_list = [] 15 lock = threading.Lock() #生成全局锁 16 for i in range(100): 17 t = threading.Thread(target=addNum) 18 t.start() 19 print "threading num: %s \n" %threading.active_count() #查看线程数 20 thread_list.append(t) 21 22 for t in thread_list: # 等待所有线程执行完毕 23 t.join() 24 25 print('final num:', num)
死锁
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程成为死锁进程。
1 import threading 2 import time 3 4 mutexA = threading.Lock() 5 mutexB = threading.Lock() 6 7 class MyThread(threading.Thread): 8 def __init__(self): 9 threading.Thread.__init__(self) 10 11 def run(self): 12 self.fun1() 13 self.fun2() 14 15 def fun1(self): 16 mutexA.acquire() 17 print("fun1 I am %s,get res:%s---%s" % (self.name, "ResA", time.time())) 18 19 mutexB.acquire()#线程2卡在这里,获取不到锁B,线程2此时已经有锁A 20 print("fun1 I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 21 mutexB.release() 22 print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResB", time.time())) 23 mutexA.release() 24 print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResA", time.time())) 25 26 def fun2(self): 27 mutexB.acquire() 28 print("fun2 I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 29 30 time.sleep(0.3) #这里整个进程sleep, 31 print("sleep fun2 I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 32 33 mutexA.acquire()#线程1卡在这一步,线程1此时已经有锁b 34 print("fun2 I am %s,get res:%s---%s" % (self.name, "ResA", time.time())) 35 36 mutexA.release() 37 print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResA", time.time())) 38 39 mutexB.release() 40 print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResB", time.time())) 41 42 43 if __name__ == '__main__': 44 print("start------------%s", time.time()) 45 for i in range(0, 10): #一次循环,代表一个线程 46 print "%s \n" %i 47 my_thread = MyThread() 48 my_thread.start()
运行结果
0
fun1 I am Thread-1,get res:ResA---1551263063.35 #线程1获得锁B
fun1 I am Thread-1,get res:ResB---1551263063.35 #线程1获得锁B
FUN1 I am Thread-1, release res:ResB---1551263063.35 #线程1释放锁B
FUN1 I am Thread-1, release res:ResA---1551263063.35 #线程1释放锁A
fun2 I am Thread-1,get res:ResB---1551263063.35 #线程1获得锁B
1
fun1 I am Thread-2,get res:ResA---1551263063.35 #线程2释放锁A
2
..
9
sleep fun2 I am Thread-1,get res:ResB---1551263065.35 #线程2释放锁B
总结:
1、线程1已有B锁,准备获取A锁
2、线程2已有A锁,准备获取B锁
1和2同一时刻,因此互相等待
RLock(递归锁)
说白了就是在一个大锁中还要再包含子锁
在python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
1 Rlock = threading.RLock() 2 class MyThread(threading.Thread): 3 def __init__(self): 4 threading.Thread.__init__(self) 5 6 def run(self): 7 self.fun1() 8 self.fun2() 9 10 def fun1(self): 11 Rlock.acquire() # 如果锁被占用,则阻塞在这里,等待锁的释放 12 print("I am %s,get res:%s---%s" % (self.name, "ResA", time.time())) 13 Rlock.acquire() # count=2 14 print("I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 15 16 Rlock.release() # count-1 17 Rlock.release() # count-1=0 18 19 def fun2(self): 20 Rlock.acquire() # count=1 21 print("I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 22 time.sleep(0.2) 23 24 Rlock.acquire() # count=2 25 print("I am %s,get res:%s---%s" % (self.name, "ResA", time.time())) 26 Rlock.release() # coun-1 27 Rlock.release() # count-1 28 29 30 if __name__ == '__main__': 31 print("start-----------%s" % time.time()) 32 for i in range(0, 10): 33 my_thread = MyThread() 34 my_thread.start()
Semaphore(信号量)
原理:来限制一个时间点内的线程数量。比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
1 def run(n): 2 semaphore.acquire() 3 time.sleep(1) 4 print("run the thread: %s\n" %n) 5 semaphore.release() 6 7 if __name__ == '__main__': 8 num= 0 9 semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行 10 for i in range(20): 11 t = threading.Thread(target=run,args=(i,)) 12 t.start() 13 14 while threading.active_count() != 1: 15 pass #print threading.active_count() 16 else: 17 print('----all threads done---') 18 print(num)
Events
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
红绿灯
1 import threading,time 2 import random 3 def light(): 4 if not event.isSet(): 5 event.set() #wait就不阻塞 #绿灯状态 6 count = 0 7 while True: 8 if count < 10: 9 print('\033[42;1m--green light on---\033[0m') 10 elif count <13: 11 print('\033[43;1m--yellow light on---\033[0m') 12 elif count <20: 13 if event.isSet(): 14 event.clear() 15 print('\033[41;1m--red light on---\033[0m') 16 else: 17 count = 0 18 event.set() #打开绿灯 19 time.sleep(1) 20 count +=1 21 def car(n): 22 while 1: 23 time.sleep(random.randrange(10)) 24 if event.isSet(): #绿灯 25 print("car [%s] is running.." % n) 26 else: 27 print("car [%s] is waiting for the red light.." %n) 28 if __name__ == '__main__': 29 event = threading.Event() 30 Light = threading.Thread(target=light) 31 Light.start() 32 for i in range(3): 33 t = threading.Thread(target=car,args=(i,)) 34 t.start()
员工过门禁
1 import threading 2 import time 3 import random 4 5 def door(): 6 door_open_time_counter = 0 7 while True: 8 if door_swiping_event.is_set(): 9 print("\033[32;1mdoor opening....\033[0m") 10 door_open_time_counter +=1 11 else: 12 print("\033[31;1mdoor closed....,please swipe to open.\033[0m") 13 door_open_time_counter = 0 #清空计时器 14 door_swiping_event.wait() 15 if door_open_time_counter > 3:#门开了已经3s了,该关了 16 door_swiping_event.clear() 17 time.sleep(0.5) 18 19 def staff(n): 20 print("staff [%s] is comming..." % n ) 21 while True: 22 if door_swiping_event.is_set(): 23 print("\033[34;1mdoor is opened, staff [%s] passing.....over!!\033[0m" % n ) 24 break 25 else: 26 print("STAFF [%s] sees door got closed, swipping the card....." % n) 27 door_swiping_event.set() 28 print("after set,,, staff [%s]--------" %n,door_swiping_event.is_set()) 29 # time.sleep(0.5) 30 31 door_swiping_event = threading.Event() #设置事件 32 door_thread = threading.Thread(target=door) 33 door_thread.start() 34 for i in range(5): 35 p = threading.Thread(target=staff,args=(i,)) 36 time.sleep(random.randrange(3)) 37 p.start()
运行结果 door closed....,please swipe to open. staff [0] is comming... STAFF [0] sees door got closed, swipping the card..... ('after set,,, staff [0]--------', True) door is opened, staff [0] passing.....over!! door opening.... door opening.... door opening.... door opening.... staff [1] is comming... STAFF [1] sees door got closed, swipping the card..... ('after set,,, staff [1]--------'staff [2] is comming..., Truedoor is opened, staff [2] passing.....over!!) door is opened, staff [1] passing.....over!! door opening.... door closed....,please swipe to open. staff [3] is comming... STAFF [3] sees door got closed, swipping the card..... ('after set,,, staff [3]--------', True) door is opened, staff [3] passing.....over!! door opening.... door opening.... staff [4] is comming... door is opened, staff [4] passing.....over!! door opening.... door opening.... door closed....,please swipe to open.