python-day36--并发编程之多线程
十三、死锁、递归锁
1.所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Lock,Thread 2 import time 3 mutexB=Lock() 4 mutexA=Lock() 5 class MyThread(Thread): 6 def run(self): 7 self.f1() 8 self.f2() 9 10 def f1(self): 11 mutexA.acquire() 12 print('%s拿到了A锁' %self.name) 13 mutexB.acquire() 14 print('%s拿到了B锁' %self.name) 15 mutexB.release() 16 mutexA.release() 17 18 def f2(self): 19 mutexB.acquire() 20 print('%s拿到了B锁' %self.name) 21 time.sleep(1) 22 mutexA.acquire() 23 print('%s拿到了A锁' %self.name) 24 mutexA.release() 25 mutexB.release() 26 27 if __name__ == '__main__': 28 for i in range(10): 29 t=MyThread() 30 t.start() 31 32 # 结果 33 # Thread-1拿到了A锁 34 # Thread-1拿到了B锁 35 # Thread-1拿到了B锁 36 # Thread-2拿到了A锁 37 。。。。 卡住了
2.解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Thread,RLock 2 import time 3 mutexB=mutexA=RLock() 4 class MyThread(Thread): 5 def run(self): 6 self.f1() 7 self.f2() 8 9 def f1(self): 10 mutexA.acquire() 11 print('\033[32m%s 拿到A锁' %self.name) 12 mutexB.acquire() 13 print('\033[45m%s 拿到B锁' %self.name) 14 mutexB.release() 15 mutexA.release() 16 17 def f2(self): 18 mutexB.acquire() 19 print('\033[32m%s 拿到B锁' %self.name) 20 time.sleep(1) 21 mutexA.acquire() 22 print('\033[45m%s 拿到A锁' %self.name) 23 mutexA.release() 24 mutexB.release() 25 26 if __name__ == '__main__': 27 for i in range(2): 28 t=MyThread() 29 t.start() 30 31 # 结果: 32 # Thread-1 拿到A锁 33 # Thread-1 拿到B锁 34 # Thread-1 拿到B锁 35 # Thread-1 拿到A锁 36 # Thread-2 拿到A锁 37 # Thread-2 拿到B锁 38 # Thread-2 拿到B锁 39 # Thread-2 拿到A锁
十四、信号量Semaphore
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Thread,Semaphore,currentThread 2 import time,random 3 sm=Semaphore(5) #限制最大连接数为5 4 def task(): 5 sm.acquire() 6 print('%s 上厕所' %currentThread().getName()) 7 time.sleep(random.randint(1,3)) 8 print('%s 走了' %currentThread().getName()) 9 sm.release() 10 if __name__ == '__main__': 11 for i in range(20): 12 t=Thread(target=task) 13 t.start()
与进程池是完全不同的概念,进程池Pool(4),最大只能产生4个进程,而且从头到尾都只是这 四个进程,不会产生新的,而信号量是产生一堆线程/进程
十五、Event事件
1 event.isSet():返回event的状态值; 2 3 event.wait():如果 event.isSet()==False将阻塞线程; 4 5 event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; 6 7 event.clear():恢复event的状态值为False。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Thread,Event,currentThread 2 import time 3 e=Event() 4 5 def traffic_lights(): 6 time.sleep(5) 7 e.set() 8 9 def car(): 10 print('\033[45m%s 等' %currentThread().getName()) 11 e.wait() 12 print('\033[45m%s 跑' %currentThread().getName()) 13 14 15 if __name__ == '__main__': 16 for i in range(10): 17 t=Thread(target=car) 18 t.start() 19 traffic_thread=Thread(target=traffic_lights) 20 traffic_thread.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Thread,Event,currentThread 2 import time 3 e=Event() 4 def conn_mysql(): 5 count=1 6 while not e.is_set(): 7 if count > 3: 8 raise ConnectionError('尝试链接的次数过多') 9 print('\033[45m%s 第%s次尝试' %(currentThread().getName(),count)) 10 e.wait(timeout=1) 11 count+=1 12 print('\033[45m%s 开始链接' %currentThread().getName()) 13 14 def check_mysql(): 15 print('\033[45m%s 检测mysql...' %currentThread().getName()) 16 time.sleep(4) #超时了 17 e.set() 18 if __name__ == '__main__': 19 t=Thread(target=check_mysql) 20 t.start() 21 for i in range(3): 22 t=Thread(target=conn_mysql) 23 t.start()
十六、定时器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Timer 2 3 def hello(n): 4 print("hello, world",n) 5 6 t = Timer(3, hello,args=(123,)) #3秒后运行hello函数, 可以传参数 7 t.start()
十七、线程queue
queue队列 :使用import queue,用法与进程Queue一样
queue.Queue
#先进先出 #队列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import queue 2 3 q=queue.Queue() 4 q.put('first') 5 q.put('second') 6 q.put('third') 7 8 print(q.get()) 9 print(q.get()) 10 print(q.get()) 11 ''' 12 结果(先进先出): 13 first 14 second 15 third 16 '''
queue.LifoQueue
#last in fisrt out #堆栈
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import queue 2 3 q=queue.LifoQueue() 4 q.put('first') 5 q.put('second') 6 q.put('third') 7 8 print(q.get()) 9 print(q.get()) 10 print(q.get()) 11 ''' 12 结果(后进先出): 13 third 14 second 15 first 16 '''
queue.PriorityQueue
#存储数据时可设置优先级的队列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import queue 2 3 q=queue.PriorityQueue() 4 #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高 5 q.put((20,'a')) 6 q.put((10,'b')) 7 q.put((30,'c')) 8 9 print(q.get()) 10 print(q.get()) 11 print(q.get()) 12 ''' 13 结果(数字越小优先级越高,优先级高的优先出队): 14 (10, 'b') 15 (20, 'a') 16 (30, 'c') 17 '''