簡體   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