使用名称创建异步锁

Create asyncio lock with name

我们能否以某种方式使用 asyncio 创建类似于 Redis 命名锁行为方式的东西?

在 Redis 中我们可以使用 lock with key,这样如果你改变密钥,它就会改变锁。
这样我们就可以为不同的钥匙使用不同的锁,这将加快速度。

with r.lock('my_lock'):
    # ...

我们可以使用 asyncio 做类似的事情吗?谢谢!

使用函数式方法和字典,您可以执行如下操作:

import asyncio
from contextlib import asynccontextmanager  # python 3.7+
from random import randint

locks = {}


@asynccontextmanager
async def get_lock(id):
    if not locks.get(id):
        locks[id] = asyncio.Lock()
    async with locks[id]:
        yield
    if len(locks[id]._waiters) == 0 and is_obsolete(id):
        del locks[id]
    print(len(locks))


def is_obsolete(id):
    return True  # whatever test you need


async def main():
    tasks = []
    for _ in range(100):
        id = randint(0, 10)
        tasks.append(asyncio.create_task(do_task(id)))
        await asyncio.sleep(0.01)
    await asyncio.gather(*[task for task in tasks if not task.done()])


async def do_task(id):
    async with get_lock(id):

        await asyncio.sleep(randint(0, 10) / 10)


asyncio.run(main())

一旦没有剩余的等待任务和可能基于您的应用程序的另一个条件(比如资源仍然存在),锁就会被移除。 这应该适用于任何可哈希 id。例如 pathlib.Path 很好。

我不知道它对您来说需要多高的效率。嵌套的上下文管理器可能是一个无赖。我想替代方案是 subclass asyncio.Lock 覆盖 __aexit__ 并创建某种管理器 class 但我认为这会更冗长。