繁体   English   中英

带有'with'语句的非阻塞锁

[英]non-blocking lock with 'with' statement

据我所知,如果lock已经被另一个线程获取,下面的代码将被阻塞。

似乎可以通过lock.acquire(0)实现非阻塞,但我必须使用try-finallywith不是块。

lock = threading.Lock()

def func():
 with lock:
  # do something...

有没有办法实现非阻塞锁获取?

@contextmanager
def nonblocking(lock):
    locked = lock.acquire(False)
    try:
        yield locked
    finally:
        if locked:
            lock.release()

lock = threading.Lock()
with nonblocking(lock) as locked:
    if locked:
        do_stuff()

有没有办法实现非阻塞锁获取?

是的。 如果无法立即获得锁,只需引发异常。 就像是:

@contextlib.contextmanager
def non_blocking_lock(lock=threading.Lock()):
    if not lock.acquire(blocking=False):
        raise WouldBlockError
    try:
        yield lock
    finally:
        lock.release()

用法:

with non_blocking_lock():
    # run with the lock acquired

您可以实现自己的锁定对象,其行为符合您的要求:

class MyLock(object):
    def __init__(self):
        self._lock = threading.Lock()
        self._locked = False
    def __enter__(self):
        locked = self._lock.acquire(False)
        self._locked = locked
        return locked
    def __exit__(self, *args):
        if self._locked:
            self._lock.release()
            self._locked = False

我用这段代码测试了它:

In [5]: ml = MyLock()
In [6]: with ml as aq:
   ...:     print "got it?!", aq
   ...:     with ml as try2:
   ...:         print "got it second time???", try2

输出是:

got it?! True
got it second time??? False

您还可以实现使用内锁的锁定和解锁方法,但您可以获得图片,以满足您的需求。

如果您需要一个以非阻塞方式获取锁的上下文管理器,但仍会重试直到最终可以获取锁,您可以这样做:

@contextlib.contextmanager
def non_blocking_lock(lock : threading.Lock):
    # Waits as long as the lock can not be acquired, but releases the GIL in the meanwhile
    while not lock.acquire(blocking=False):
        pass

    try:
        yield   # Lock has been successfully acquired
    finally:
        lock.release()

它可以像普通的锁上下文管理器一样使用:

class TestClass:
    def __init__(self):
         self._lock = threading.Lock()
    
    def method(self):
         with non_blocking_lock(self._lock):
         # do something that should be only done from one thread at once

...不同的是,锁是非阻塞的,并且在锁被释放之前不会持有 GIL。 我用它来修复一些死锁。

与其他解决方案的不同之处在于,代码最终会被执行,并且上下文管理器不会在无法获取锁时简单地返回 false 或抛出异常。

如果您发现此解决方案有任何警告,请纠正我。

锁的全部意义在于确保程序的某些部分一次只能由一个线程或进程执行。 这是通过阻止任何线程/进程尝试获取锁而其他东西持有它来实现的。

如果你不想获取锁来阻塞,你为什么要首先使用锁? 大概是为了让您在等待时可以做其他事情?

要尝试获取锁l而不阻塞,请调用l.acquire(blocking=False) 如果没有获得锁,这将立即返回False 如果获得了锁它会返回True ,并且您将继续持有锁,直到您调用它的release()方法。

然而,这种形式对于with语句并不是特别有用。 通常,您希望受控代码( with之后的缩进套件)仅在获得锁时运行。 不去查询有没有,采取两种替代的行动。

如果你想使用带有非阻塞锁的 with 语句,你也可以先检查它是否被锁定。 如果是,那么您就不要进入 with 块。 例如:

lock = threading.Lock()

def func():
    if not lock.locked():
        with lock:
            # do something...

暂无
暂无

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

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