如何使用threading锁定异步功能,同时可以从多个线程访问对象

问题描述

我想在异步函数中使用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__

解决方法

您不能将threading.Lock传递给async with,因为它不是为异步使用而设计的,它是一个阻塞原语。更重要的是,async with threading.Lock()即使有效也没有任何意义,因为您将获得一个全新的锁,它将永远成功。为了使锁定有意义,您必须在多个线程之间具有 shared 锁,例如存储在对象的属性中,或以其他方式与对象相关联。该答案的其余部分将假定您在线程之间共享threading.Lock

由于threading.Lock始终处于阻塞状态,因此可以从asyncio中使用它的唯一方法是在专用线程中获取它,从而中止当前协程的执行,直到获得锁为止。 run_in_executor事件循环方法已经涵盖了此功能,您可以应用该方法:

# 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(None,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()

您可以通过创建异步上下文管理器来使其更加美观(且异常安全):

@contextlib.asynccontextmanager
async def async_lock(lock):
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None,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 ...

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...