使用'进行非阻塞锁定声明

时间:2015-07-19 12:59:41

标签: python multithreading locking

据我所知,如果另一个线程已经获取lock,则会阻止以下代码。

lock.acquire(0)似乎可以实现非阻止,但我必须使用try-finally阻止with阻止。

lock = threading.Lock()

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

是否有任何方法可以实施非阻止锁定获取?

5 个答案:

答案 0 :(得分:8)

  

是否有任何方法可以实现非阻塞锁定?

是。如果无法立即获取锁定,只需引发异常即可。类似的东西:

@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

答案 1 :(得分:5)

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

答案 2 :(得分:2)

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

DECLARE @properID VARCHAR(50)
SET @properID = 'AL*12345*B001*1'

;WITH CTE_REC AS 
 (
  SELECT 1 as R ,CHARINDEX('*', @properID) as Pos , @properID ProperId
   UNION ALL
  SELECT R + 1 , CHARINDEX ('*' ,@properID , Pos + 1) , ProperId
   FROM CTE_REC
   WHERE Pos > 0 

 )
 SELECT ProperId, 
  SUBSTRING(ProperId , 0 , Pos) RequiredProperId
  FROM CTE_REC
  WHERE Pos > 0 
  AND R = 3 --Change this condition accordingly if you need only until nth occurance

我用这段代码测试了它:

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

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

答案 3 :(得分:1)

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

如果您不想获取锁定阻止,为什么您首先使用锁定?大概是这样你在等待的时候可以做点别的事情吗?

要尝试获取锁定l而不阻止,请致电l.acquire(blocking=False)。如果未获取锁定,则会立即返回False。如果锁定 ,则会返回True,您将继续保持锁定,直到您调用其release()方法。

但是,此表单对with语句并不特别有用。通常,您希望受控代码(with之后的缩进套件)仅在获取锁定时运行。不要询问是否有,并采取两种替代行动。

答案 4 :(得分:1)

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

@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 或抛出异常。

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