1 回答

TA贡献1851条经验 获得超4个赞
您不能将 a 传递threading.Lock
给async with
,因为它不是为异步使用而设计的,它是一个阻塞原语。更重要的是,async with threading.Lock()
即使它确实有效也没有意义,因为您将获得一把全新的锁,它总是会成功。为了使锁定有意义,您必须在多个线程之间共享一个锁,例如存储在对象的属性中,或以另一种方式与对象相关联。这个答案的其余部分将假设您在threading.Lock
线程之间共享。
由于threading.Lock
总是阻塞,因此您可以从 asyncio 使用它的唯一方法是在专用线程中获取它,暂停当前协程的执行,直到获取锁。此功能已包含在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 ...
当然,在 asyncio 之外你不会使用其中的任何一个,你只是直接获取锁:
# lock is a threading.Lock shared between threads
with lock:
... access the object ...
请注意,我们使用单独的线程池而不是传递None给run_in_executor()重用默认池。这是为了避免在持有锁的函数本身需要访问线程池以供其他用途的情况下出现死锁run_in_executor()。通过保持线程池私有,我们避免了因其他人使用同一池而导致死锁的可能性。
添加回答
举报