我想在异步函数中使用threading.Lock()
,asyncio.Lock()
不是线程安全的,所以不能执行with await asyncio.Lock():
。我需要使用threading.Lock()
的原因是因为这个对象可能被多个踏板访问,因此它在web应用程序中使用,运行它的服务器可以旋转许多线程。这样做的有效方法是什么?到目前为止,我尝试了一个使用锁的简单函数:
1)
async def main():
with await threading.Lock():
a = 6
return a
TypeError: object _thread.lock can't be used in 'await' expression
async def main():
async with threading.Lock():
a = 1564666546
return a
AttributeError: __aexit__
发布于 2020-08-15 10:50:44
您不能将threading.Lock
传递给async with
,因为它不是为异步使用而设计的,它是一个阻塞基元。更重要的是,async with threading.Lock()
没有意义,即使它确实有效,因为您将获得一个全新的锁,这将永远成功。为了使锁定有意义,您必须在多个线程之间共享一个锁,例如存储在对象的属性中,或者以另一种方式与对象关联。这个答案的其余部分将假设您在线程之间共享一个threading.Lock
。
由于threading.Lock
总是阻塞,所以您可以从异步中使用它的唯一方法是在一个专用线程中获取它,在获得锁之前暂停当前协同线的执行。run_in_executor
事件循环方法已经涵盖了此功能,您可以应用该方法:
_pool = concurrent.futures.ThreadPoolExecutor()
async def work(lock, other_args...):
# lock is a threading.Lock shared between threads
loop = asyncio.get_event_loop()
# Acquire the lock in a worker thread, suspending us while waiting.
await loop.run_in_executor(_pool, lock.acquire)
... access the object with the lock held ...
# Can release directly because release() doesn't block and a
# threading.Lock can be released from any thread.
lock.release()
通过创建异步上下文管理器,您可以更优雅地使用它(以及异常安全):
_pool = concurrent.futures.ThreadPoolExecutor()
@contextlib.asynccontextmanager
async def async_lock(lock):
loop = asyncio.get_event_loop()
await loop.run_in_executor(_pool, lock.acquire)
try:
yield # the lock is held
finally:
lock.release()
然后您可以按以下方式使用它:
# lock is a threading.Lock shared between threads
async with async_lock(lock):
... access the object with the lock held ...
当然,在异步之外,您不会使用其中的任何一个,您只需直接获取锁:
# lock is a threading.Lock shared between threads
with lock:
... access the object ...
注意,我们使用一个单独的线程池,而不是将None
传递给run_in_executor()
来重用默认池。这是为了避免在持有锁的函数本身需要访问线程池以供其他run_in_executor()
使用的情况下出现死锁。通过保持线程池的私有性,我们避免了其他人通过使用同一个池而导致死锁的可能性。
https://stackoverflow.com/questions/63420413
复制相似问题