简体   繁体   English

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

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

As far as I know, the following code will be blocked if lock is already acquired by another thread.据我所知,如果lock已经被另一个线程获取,下面的代码将被阻塞。

It seems that non-blocking can be implemented by lock.acquire(0) , but instead I have to use try-finally block instead with block.似乎可以通过lock.acquire(0)实现非阻塞,但我必须使用try-finallywith不是块。

lock = threading.Lock()

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

Is there any method to implement non-blocking lock acquisition?有没有办法实现非阻塞锁获取?

@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()

Is there any method to implement non-blocking lock acquisition?有没有办法实现非阻塞锁获取?

Yes.是的。 Just raise an exception if the lock can't be acquired immediately.如果无法立即获得锁,只需引发异常。 Something like:就像是:

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

Usage:用法:

with non_blocking_lock():
    # run with the lock acquired

You can implement your own lock-like object that behaves as you wish: 您可以实现自己的锁定对象,其行为符合您的要求:

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

I tested it with this code: 我用这段代码测试了它:

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

Output is: 输出是:

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

You can also implement lock and unlock methods that use the inner lock, but you get the picture, fit it to your needs. 您还可以实现使用内锁的锁定和解锁方法,但您可以获得图片,以满足您的需求。

If you need a context manager that acquires a lock in a non-blocking manner, but still retries until the lock can finally be acquired, you could do like this:如果您需要一个以非阻塞方式获取锁的上下文管理器,但仍会重试直到最终可以获取锁,您可以这样做:

@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()

It can be used exactly like the normal lock context manager:它可以像普通的锁上下文管理器一样使用:

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

... with the difference, that the lock is non-blocking and doesn't hold the GIL until the lock is released. ...不同的是,锁是非阻塞的,并且在锁被释放之前不会持有 GIL。 I used it to fix some deadlocks.我用它来修复一些死锁。

The difference to the other solutions is, that the code eventually gets executed and the context manager does not simply return a false or throw an exception when the lock couldn't be acquired.与其他解决方案的不同之处在于,代码最终会被执行,并且上下文管理器不会在无法获取锁时简单地返回 false 或抛出异常。

Correct me if you see any caveats with this solution.如果您发现此解决方案有任何警告,请纠正我。

The whole point of a lock is to ensure that certain sections of your program will only be executed by one thread or process at a time.锁的全部意义在于确保程序的某些部分一次只能由一个线程或进程执行。 This is achieved by blocking any threads/processes trying to acquire the lock while something else holds it.这是通过阻止任何线程/进程尝试获取锁而其他东西持有它来实现的。

If you don't want acquiring the lock to block, why are you using a lock in the first place?如果你不想获取锁来阻塞,你为什么要首先使用锁? Presumably so that you can do something else while you wait?大概是为了让您在等待时可以做其他事情?

To attempt to acquire the a lock l without blocking, call l.acquire(blocking=False) .要尝试获取锁l而不阻塞,请调用l.acquire(blocking=False) This will immediately return False if the lock was not acquired.如果没有获得锁,这将立即返回False If the lock was acquired it returns True , and the you will continue to hold the lock until you call its release() method.如果获得了锁它会返回True ,并且您将继续持有锁,直到您调用它的release()方法。

This form, however, isn't particularly useful with the with statement.然而,这种形式对于with语句并不是特别有用。 Usually you want the controlled code (the indented suite after the with ) to run only when the lock has been acquired.通常,您希望受控代码( with之后的缩进套件)仅在获得锁时运行。 not to query whether it has or not and take two alternative actions.不去查询有没有,采取两种替代的行动。

If you want to use the with statement with a non-blocking lock, you could also first check if it is locked.如果你想使用带有非阻塞锁的 with 语句,你也可以先检查它是否被锁定。 If it is, then you don't go into the with block.如果是,那么您就不要进入 with 块。 Eg:例如:

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