求各位指点一下这里 Python lock 的用法,我想了很久还是不理解。

2020-06-17 06:36:43 +08:00
 oooolongtea
这是一道 leetcode 题目的答案: https://leetcode.com/problems/print-foobar-alternately/discuss/603570/python3%3A-40ms-86.64-Faster-100-Less-memory-using-threading.Lock

用两个 lock 去控制 def foo()和 def bar()运行的顺序。
我不理解的地方是:
在__init__里面,self.bar_lock.acquire()已经把 self.bar_lock 给锁上了。
运行的时候,foo()和 bar()会同时进行,但是因为在 bar()里面,有一个 self.bar_lock.acquire(),这行代码之前已经运行过了,因此在这里又再一次把 self.bar_lock 给 acquire 住了,等于是 self.bar_lock 被 acquire 了两次。
我上网查了一下,遇到这个情况,第二次 acquire 的时候,如果发现这个 self.bar_lock 之前已经被 acquire,那么这个第二次的 acquire 就会停在这里,等到 self.bar_lock 被 release 。
那么我的问题来了,如果被 acquire 了两次的话,我们需要不要 release 两次? 看代码,我们不需要 release 两次。
2129 次点击
所在节点    Python
6 条回复
noqwerty
2020-06-17 06:45:28 +08:00
建议读一下文档: https://docs.python.org/3/library/threading.html#threading.Lock.acquire

另外也可以从语义上去理解,锁 (Lock) 只有上锁和解锁两种状态,不存在你说的开两次锁的情况。
cigarzh
2020-06-17 07:03:17 +08:00
你说的那玩意是 threading.RLock()
js8510
2020-06-17 09:08:59 +08:00
哇。leetcode 都有一千多题了呀。我还真在 在生产环境见过类似的情况。但是实现和你发的这个有不同


作者的意图应该是这样,
···
from threading import Lock

class FooBar:
def __init__(self, n):
self.n = n
self.foo_lock = Lock()
self.bar_lock = Lock()
self.bar_lock.acquire() # <---- 1


def foo(self, printFoo: 'Callable[[], None]') -> None:
for i in range(self.n):
self.foo_lock.acquire() # <---- 2 | # <---- 5, blocked here and wait for foo_lock
# printFoo() outputs "foo". Do not change or remove this line.
printFoo() # <---- 3
self.bar_lock.release() # <---- 4


def bar(self, printBar: 'Callable[[], None]') -> None:
for i in range(self.n):
self.bar_lock.acquire() # <---- 2 and blocked here and wait for bar_lock
# printBar() outputs "bar". Do not change or remove this line.
printBar() # <---- 5
self.foo_lock.release() # <---- 6
···
lpts007
2020-06-17 10:12:38 +08:00
```python
from threading import Lock

```
gzfrankie
2020-06-17 13:15:32 +08:00
因为那段代码,foo_lock 和 bar_lock 是两个不同的 lock 。
流程:
1. 在__INIT__里锁了 bar_lock
2.这时候两个进程同时开始,bar 锁在 self.bar_lock.acquire()那里不动了
3.foo 正常运行,运行结束了之后 self.bar_lock.release()解锁 bar_lock ;运行到第二次 foo_lock.acquire(),锁住不动
4.bar_lock 被解锁,bar 继续运行,到最后解锁 foo_lock
5.foo_lock 被解锁,foo 继续运行

所以是两个 lock,在 foo 里解 bar_lock,在 bar 里解 foo_lock,不存在同一个 threadacquire 了两次同一个 lock 的情况
gzfrankie
2020-06-17 13:27:58 +08:00
在看了一下你的疑问,补充一下:
"那么我的问题来了,如果被 acquire 了两次的话,我们需要不要 release 两次? 看代码,我们不需要 release 两次。"
不需要 release 两次,因为
1. 如果第一次 acquire()成功,acquire()返回 True,程序运行
2.第二次 acquire(),由于 lock 并没有被 release,程序被停在 acquire 那行;想让程序继续运行,需要在别的地方把这个 lock release 掉。
3.release 一个已被解锁的 lock,python 会 throw RuntimeError

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/682213

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX