学科分类
目录

可重入锁

为了避免因同一线程多次使用互斥锁造成的死锁,threading模块中提供了RLock类。RLock类代表可重入锁,它允许同一线程多次锁定和多次释放。通过RLock类的构造方法可以创建一个可重入锁,示例如下:

r_lock = RLock()

RLock类中包含以下三个重要的属性:

(1) _block,表示内部的互斥锁。

(2) _owner,表示可重入锁的持有者的线程ID。

(3) _count,表示计数器,用于记录锁被持有的次数。针对RLock对象的持有线程(属主线程),每上锁一次计数器就+1,每解锁一次就-1。若计数器为0,则释放内部的锁,这时其他线程可以获取内部的互斥锁,继而获取RLock对象。

可重入锁的实现原理是通过为每个内部锁关联计数器和属主线程。当计数器为0时,内部锁处于非锁定状态,可以被其它线程持有;当线程持有一个处于非锁定状态的锁时,它将被记录为锁的持有线程,计数器置为1。

RLock类中使用acquire()和release()方法锁定和释放,具体用法与Lock类相似,此处不再过多赘述。

下面自定义一个线程类MyThread,在该类重写的run()方法中将可重入锁多次上锁定和释放,并在锁定期间修改全局变量num的值,之后启动五个线程执行程序,代码如下:

 1  from threading import Thread, RLock
 2  import time
 3  num = 0             # 定义全局变量
 4  r_lock = RLock()        # 创建可重入锁r_lock
 5  class MyThread(Thread):
 6    def run(self):
 7      global num
 8      time.sleep(1)
 9      if r_lock.acquire(): # 若r_lock处于锁定状态
 10       num = num + 1   # 修改全局变量
 11       msg = self.name + '将num改为' + str(num)
 12       print(msg)
 13       r_lock.acquire()  # 再次上锁
 14       r_lock.release()  # r_lock解锁
 15       r_lock.release()  # r_lock再次解锁
 16 if __name__ == '__main__':
 17   for i in range(5):     # 创建5个线程
 18     t = MyThread()
 19     t.start()

上述代码中,第3、4行分别定义一个全局变量num和全局可重入锁r_lock;第5~15行定义了一个线程类MyThread,在该类重写的run()方法中判断可重入锁的状态,若该锁已经处于锁定状态,首先将全局变量num的值加1后打印,然后对r_lock上锁,最后连续调用两次release()方法解锁。第17~19行创建了5个线程执行任务。

运行代码,结果如下所示:

Thread-1将num改为1
Thread-3将num改为2
Thread-2将num改为3
Thread-5将num改为4
Thread-4将num改为5

由以上结果可知,线程Thread-1优先执行,将num的值改为1;线程Thread-3第二个执行,将num的值改为2......线程Thread-4最后执行,将num的值改为5。由此表明,可重入锁同样能够解决多线程访问共享数据的冲突问题。

点击此处
隐藏目录