简体   繁体   English

获取多处理。如果非阻塞或超时,则在 with 语句中锁定

[英]Acquire a multiprocessing.Lock in a with statement if non-blocking or with timeout

With a normal multiprocessing.Lock (or threading.Lock ) you can simplify the following code:使用普通的multiprocessing.Lock (或threading.Lock )您可以简化以下代码:

lock = multiprocessing.Lock()
lock.acquire()

try:
    ...
finally:
    lock.release()

into:进入:

with lock:
    ...

However, can I still use a context manager when I want to pass some arguments to lock.acquire(...) , such as block= or timeout= ?但是,当我想将一些 arguments 传递给lock.acquire(...)时,我是否仍然可以使用上下文管理器,例如block=timeout= For example, I have code like this:例如,我有这样的代码:

lock_success = lock.acquire(block=False)

if not lock_success:
    return

try:
    ...
finally:
    lock.release()

I don't see a way to pass this argument to the context manager (since it's in the acquire call and not the constructor).我看不到将此参数传递给上下文管理器的方法(因为它在acquire调用中而不是构造函数中)。

(The idea is that the with -block would get skipped if the lock cannot be acquired.) (想法是,如果无法获取锁,将跳过with块。)

Analogously for threading.Lock which provides a similar API.类似于threading.Lock ,它提供了类似的 API。

TLDR; TLDR; you cannot use the built-in lock context manager, but it can still be done fairly cleanly.你不能使用内置的锁上下文管理器,但它仍然可以相当干净地完成。

It could almost work because Lock.__enter__() does return the value returned from the call to acquire() which should be the boolean success or failure of getting the lock.它几乎可以工作,因为Lock.__enter__()确实返回调用acquire()返回的值,该值应该是 boolean 获取锁的成功或失败。

l = Lock()
with l as success:
    if success:
        print("run some code")
    else:
        print("skip the code")

Frustratingly however it is not possible to pass arguments to the internal call to acquire (hardcoded args here ).然而令人沮丧的是,不可能将 arguments 传递给内部调用以acquire此处为硬编码参数)。 I would recommend writing your own context manager to solve this, as it is quite straightforward:我建议您编写自己的上下文管理器来解决这个问题,因为它非常简单:

from multiprocessing import Lock
from contextlib import contextmanager

@contextmanager
def get_lock(lock, block=True, timeout=None):
    held = lock.acquire(block=block, timeout=timeout)
    try:
        yield held
    finally:
        if held:
            lock.release()

#### example usage ####

l = Lock()
#lock should be acquired so success == True
with get_lock(l) as success:
    if success:
        print("run some code")
    else:
        print("skip the code")

l.acquire()
#lock won't be acquired and block will proceed after timeout
#use the value of success to determine what to do
with get_lock(l, True, 3) as success:
    if success:
        print("run some code")
    else:
        print("skip the code")

l.release()

My suspicion is that this is not possible because of how context managers are designed in Python. What happens with a context manager (CM) is:我怀疑这是不可能的,因为 Python 中上下文管理器的设计方式。上下文管理器 (CM) 发生的情况是:

with CM:
    ...
  • The CM's __enter__ method is called调用了 CM 的__enter__方法
  • The block inside of with is run with里面的块被运行
  • The CM's __exit__ method is called调用CM的__exit__方法

Thus there is no way to "skip" the inside of the with -block, so I guess this explains the design of the Lock class. Although there are some dark magic ways around that (which should be avoided).因此没有办法“跳过” with块的内部,所以我想这解释了Lock class 的设计。尽管围绕它有一些黑暗的魔法方法(应该避免)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM