python中thread.Lock()和thread.RLock()的区别

1、Lock和RLock概念

Lock:Lock被称为原始锁,原始锁是一个在锁定时不属于特定线程的同步基元组件,它是能用的最低级的同步基元组件。原始锁处于 "锁定" 或者 "非锁定" 两种状态之一。它被创建时为非锁定状态。它有两个基本方法, acquire() 和 release() 。当状态为非锁定时, acquire() 将状态改为锁定并立即返回。当状态是锁定时, acquire() 将阻塞至其他线程调用 release() 将其改为非锁定状态,然后 acquire() 调用重置其为锁定状态并返回。 release() 只在锁定状态下调用; 它将状态改为非锁定并立即返回。如果尝试释放一个非锁定的锁,则会引发 RuntimeError 异常。锁支持上下文管理协议,即支持with语句,下文例子中会用到。

RLock:RLock被称为重入锁,若要锁定锁,线程调用其 acquire() 方法;一旦线程拥有了锁,方法将返回。若要解锁,线程调用 release() 方法。 acquire()/release() 对可以嵌套,重入锁必须由获取它的线程释放。一旦线程获得了重入锁,同一个线程再次获取它将不阻塞。只有最终 release() (最外面一对的 release() ) 将锁解开,才能让其他线程继续处理 acquire() 阻塞。;线程必须在每次获取它时释放一次。

两者使用的方法大部分还是相同的,下面根据以上红色强调部分描述一下二者的区别

①是名称的区别,一个叫原始锁,一个叫重入锁,这没啥好说的

②Lock在锁定时不属于特定线程,也就是说,Lock可以在一个线程中上锁,在另一个线程中解锁。而对于RLock来说,只有当前线程才能释放本线程上的锁,即解铃还须系铃人:在同一线程内,对RLock进行多次acquire()操作,程序不会阻塞。

2、Lock原始锁代码

import threading

lock = threading.Lock()
lock.acquire()
print(f"{lock}已被线程{threading.get_ident()}上锁了")


def func():
    lock.release()
    print(f"{lock}已被线程{threading.get_ident()}释放了")


t1 = threading.Thread(target=func)
t1.start()

'''
<locked _thread.lock object at 0x0000026CB0CA8FC0>已被线程25300上锁了
<unlocked _thread.lock object at 0x0000026CB0CA8FC0>已被线程7312释放了
'''

上面代码中,在主线程中创建锁,并上锁,但是是在t线程中释放锁,结果正常输出,说明一个线程上的锁,可以由另外线程解锁。如果把上面的锁改为RLock则报错 【RuntimeError: cannot release un-acquired lock】

RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。也就是说,下面的情况对于RLock是允许的:

3、RLock重入锁代码

import threading

rlock = threading.RLock()


def func():
    rlock.acquire()
    print(f"{rlock}上了第1把锁")
    rlock.acquire()
    print(f"{rlock}上了第2把锁")
    rlock.release()
    print(f"{rlock}释放了第2把锁")
    rlock.release()
    print(f"{rlock}释放了第1把锁")


t1 = threading.Thread(target=func)
t1.start()

'''
<locked _thread.RLock object owner=28360 count=1 at 0x000001C00D256FC0>上了第1把锁
<locked _thread.RLock object owner=28360 count=2 at 0x000001C00D256FC0>上了第2把锁
<locked _thread.RLock object owner=28360 count=1 at 0x000001C00D256FC0>释放了第2把锁
<unlocked _thread.RLock object owner=0 count=0 at 0x000001C00D256FC0>释放了第1把锁
'''

注意上面强调的同一线程中,因为对于RLock来说只有当前线程才能释放本线程上的锁,并不能在t1线程中已经执行rlock.acquire,且未释放锁的情况下,在另一个t2线程中还能执行rlock.acquire(这种情况会导致t2阻塞)

那么,既然一个线程可以通过Lock来获得一把锁,干嘛还要使用RLock去锁上加锁?考虑一下这种情况:

4、为何使用重入锁RLock

import threading

rlock = threading.RLock()


def inner():
    with rlock:
        print(f"inner: {threading.current_thread()}")


def outer():
    with rlock:
        print(f"outer: {threading.current_thread()}")
        inner()


t1 = threading.Thread(target=outer)
t2 = threading.Thread(target=outer)
t1.start()
t2.start()

'''
outer: <Thread(Thread-1, started 25108)>
inner: <Thread(Thread-1, started 25108)>
outer: <Thread(Thread-2, started 17812)>
inner: <Thread(Thread-2, started 17812)>
'''

首先只看t1线程,当执行到outer函数时,首先打印outer function:,然后用lock1给当前线程上了一把锁,然后执行inner函数,在inner里面又需要用lock1来上一把锁,如果此时用Lock的话,由于已经上了一把锁,程序会因为第二次无法获得锁而导致t1阻塞,程序阻塞之后又没法释放锁,所以会导致程序死锁。这种情况下,RLock就派上用场了。t2线程执行过程和t1一样,这里只是多加个线程看起来酷炫一些。

参考博文:

python threading模块的Lock和RLock区别_专职的博客-CSDN博客_python threading.rlock()

详解python中的Lock与RLock_ybdesire的博客-CSDN博客_python rlock

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值