day31 死锁现象、递归锁以及信号量
死锁现象
死锁:指的是某个资源被占用后一直得不到释放,导致其他需要这个资源的线程或进程进入阻塞状态
情况一:对同一把互斥锁多次执行acquire方法,将导致死锁
from threading import Lock
l = Lock()
l.acquire()
print('run....') # 打印run....
l.acquire()
print('deadlock....') # 出现死锁,不打印
解决方法:首先这样写没有意义,其次在加锁时加上超时l.acquire(timeout=10)
,超出时间后继续执行,线程不会卡死
情况二:一个共享资源要访问必须同时具备多把锁,但是这些锁被不同线程或进程所持有,就会导致线程或进程之间相互等待对方释放,从而导致进入阻塞状态
import time
from threading import Lock, Thread
A = Lock()
B = Lock()
class Mytask(Thread):
def run(self):
self.eat()
self.sleep()
def eat(self):
A.acquire()
time.sleep(1)
print(f'{self.name} got A', time.ctime())
B.acquire()
print(f'{self.name} got B', time.ctime())
B.release()
A.release()
def sleep(self):
B.acquire()
time.sleep(1)
print(f'{self.name} got B', time.ctime())
A.acquire()
print(f'{self.name} got A', time.ctime())
A.release()
B.release()
for i in range(10):
t = Mytask()
t.start()
--------------------------------------------------------------------------
Thread-1 got A Sat Jul 6 20:37:47 2019
Thread-1 got B Sat Jul 6 20:37:47 2019
Thread-2 got A Sat Jul 6 20:37:48 2019
Thread-1 got B Sat Jul 6 20:37:48 2019
# 线程2抢到了A锁没抢到B锁,线程1抢到了B锁没抢到A锁。线程2等线程1释放B锁,线程1等线程2释放A锁,双方互相等待,造成死锁现象
解决方法:1.抢锁一定按照相同的顺序去抢,2.给抢锁加上超时,如果超时则放弃执行
递归锁
在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
import time
from threading import RLock, Thread
R = RLock()
class Mytask(Thread):
def run(self):
self.eat()
self.sleep()
def eat(self):
R.acquire()
time.sleep(1)
print(f'{self.name} got A', time.ctime())
R.release()
def sleep(self):
R.acquire()
time.sleep(1)
print(f'{self.name} got B', time.ctime())
R.release()
for i in range(10):
t = Mytask()
t.start()
与互斥锁的区别:多线程之间都有互斥的效果,不同在于递归锁可以对这个锁执行多次acquire
信号量
可以限制同时并发执行公共代码的线程数量,如果限制数量为1则与普通互斥锁没有区别。并不能用来解决安全问题,而是用来限制最大的并发量
from threading import Semaphore, currentThread, Thread
def task():
s.acquire()
print('hello world')
time.sleep(2)
s.release()
s = Semaphore(3) # 限制线程的并发数,不设置默认为1
for i in range(10):
t = Thread(target=task)
t.start()