V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
oooolongtea
V2EX  ›  Python

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

  •  
  •   oooolongtea · 2020-06-17 06:36:43 +08:00 · 2185 次点击
    这是一个创建于 1620 天前的主题,其中的信息可能已经有所发展或是发生改变。
    这是一道 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 两次。
    6 条回复    2020-06-17 13:27:58 +08:00
    noqwerty
        1
    noqwerty  
       2020-06-17 06:45:28 +08:00
    建议读一下文档: https://docs.python.org/3/library/threading.html#threading.Lock.acquire

    另外也可以从语义上去理解,锁 (Lock) 只有上锁和解锁两种状态,不存在你说的开两次锁的情况。
    cigarzh
        2
    cigarzh  
       2020-06-17 07:03:17 +08:00
    你说的那玩意是 threading.RLock()
    js8510
        3
    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
        4
    lpts007  
       2020-06-17 10:12:38 +08:00
    ```python
    from threading import Lock

    ```
    gzfrankie
        5
    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
        6
    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
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1525 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:20 · PVG 01:20 · LAX 09:20 · JFK 12:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.