使用Rlock
进行线程同步
1.死锁
使用
Lock
进行线程同步的时候,可能会造成死锁.所谓死锁: 是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程. 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁.
多个线程获取多个锁,造成死锁
"""
死锁
"""
import threading,time
class MyThread(threading.Thread):
def __init__(self, lock1, lock2):
self.alock = lock1
self.block = lock2
super(MyThread, self).__init__()
def run(self):
"""acquire会返回布尔值"""
if self.alock.acquire():
print(self.name + ' up')
time.sleep(1)
if self.block.acquire():
# self.block进入阻塞状态,等待self.alock释放锁
print(self.name + ' down')
self.block.release()
self.alock.release()
class MyThread1(threading.Thread):
def __init__(self, lock1, lock2):
self.alock = lock1
self.block = lock2
super(MyThread1, self).__init__()
def run(self):
"""acquire会返回布尔值"""
if self.block.acquire():
print(self.name + 'up')
time.sleep(1)
if self.alock.acquire():
# self.alock进入阻塞状态,等待self.block释放锁
print(self.name + 'down')
self.alock.release()
self.block.release()
if __name__ == '__main__':
lock1 = threading.Lock()
lock2 = threading.Lock()
t1=MyThread(lock1, lock2)
t2=MyThread1(lock1, lock2)
t1.start()
t2.start()更简单的情况是,一个线程多次去获取锁,造成死锁
import threading
import time
num = 0
# 创建锁定池
mutex = threading.Lock()
class MyThread(threading.Thread):
def __init__(self):
super(MyThread, self).__init__()
def run(self):
global num
time.sleep(1)
# 获取锁
if mutex.acquire(1): # 设置了超时时间
num += 1
msg = "{} set num to {}".format(self.name, num)
print(msg)
mutex.acquire() # 单一线程重复获取锁,锁没有释放,变成死锁
mutex.release()
mutex.release()
def main():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
main()为了支持同一线程中,多次请求同一资源,python提供了
Rlock
可重入锁.
2.可重入锁/递归锁
Rlock
()Reentrant Lock:重入锁\递归锁说的都是它.这种锁对比Lock
有以下特点:
- 谁拿到谁释放.如果线程A拿到锁,线程B无法释放这个锁,只有A可以
- 同一线程可以多次拿到该锁,即可以多次
acquire()
.acquire()
多少次就必须release()
多少次,只有最后一次释放锁才会改变锁的状态为Unlocked
.总结:
RLock
内部维护着一个Lock
和一个counter
变量,counter
记录了acquire
的次数,从而使得资源可以被多次acquire
.直到一个线程所有的acquire
都被release
,其他的线程才能获得资源.即对于同一线程而言,是可重入锁,而对于其他线程而言,和上面的Lock没有区别.上面的例子使用
Rlock
来替代,就不会发生死锁
import threading
import time
num = 0
# 创建锁定池,RLock 可以重复的上锁
mutex = threading.RLock()
class MyThread(threading.Thread):
def __init__(self):
super(MyThread, self).__init__()
def run(self):
global num
time.sleep(1)
# 获取锁
if mutex.acquire(1): # 设置了超时时间
num += 1
msg = "{} set num to {}".format(self.name, num)
print(msg)
mutex.acquire() # 单一线程重复获取锁,锁没有释放,变成死锁
mutex.release()
mutex.release()
def main():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
main()执行结果:
Thread-1 set num to 1
Thread-4 set num to 2
Thread-2 set num to 3
Thread-3 set num to 4
Thread-5 set num to 5
3.实例
- END -构造一个
Box
类,有add()
和remove()
方法,提供了进入execute()
方法的入口,execute()
的执行由RLock()
来控制.这样每个函数的内部都实现了加锁和释放锁的操作,虽然获取多次锁,但是只有当一个线程完全释放了锁之后,另外一个线程才会去获取锁.
import threading,time
class Box:
def __init__(self, obj_lock):
self.total_items = 0
self.lock = obj_lock
def execute(self, n):
self.lock.acquire()
self.total_items += n
self.lock.release()
def add(self):
self.lock.acquire()
self.execute(1)
self.lock.release()
def remove(self):
self.lock.acquire()
self.execute(-1)
self.lock.release()
# 两个方法
def adder(box, items):
while items>0:
print('adding 1 item in the box')
box.add()
time.sleep(1)
items -= 1
def remover(box, items):
while items>0:
print('remove 1 item in the box')
box.remove()
time.sleep(1)
items -= 1
if __name__ == '__main__':
items = 5
print('putting {} items in the box'.format(items))
lock = threading.RLock()
box = Box(lock)
t1 = threading.Thread(target=adder, args=(box, items))
t2 = threading.Thread(target=remover, args=(box, items))
t1.start()
t2.start()
t1.join()
t2.join()
print('{} itesms still remain in the box'.format(box.total_items))执行结果:
putting 5 items in the box
adding 1 item in the box
remove 1 item in the box
remove 1 item in the box
adding 1 item in the box
remove 1 item in the box
adding 1 item in the box
remove 1 item in the box
adding 1 item in the box
remove 1 item in the box
adding 1 item in the box
0 itesms still remain in the box