异步函数中的细粒度资源管理?

问题描述 投票:0回答:1

使用

@asyncio.coroutine
可以编写一个细粒度的资源管理器来获取和释放共享资源(例如
Lock
或全局变量或数据库连接):

@asyncio.coroutine
def resource_context(func: Callable[..., Awaitable]):
    print("acquire")
    for i in func().__await__():
        print("release")
        yield i
        print("acquire")
    print("release")

这种细粒度的资源管理需要使用

yield
而不是
yield from
。这比上下文管理器的粒度要细得多,上下文管理器只会在开始时获取一次并在结束时释放一次。虽然上下文管理器会在我们的
func
等待时持有资源,但这种更细粒度的资源会在每次等待时释放资源。

但是

@asyncio.coroutine
从 Python 3.8 开始被弃用。我如何使用更现代的
async def
协同程序实现此功能?

注意我不是在寻找异步迭代器或异步上下文管理器:这些都不适用于此用例。我希望能够等待在

func
内部调用的任意其他异步函数,并在这些被调用者等待任何事情时释放资源。也就是说,我希望在
func
和它调用的任何代码中获取资源,但在
func
放弃控制权时暂时释放。

python asynchronous async-await python-asyncio
1个回答
1
投票

这是一个可行的答案,但它并不漂亮:

class AwaitOne(typing.NamedTuple):
    future: asyncio.Future
    def __await__(self):
        yield self.future

async def resource_context(coro: typing.Awaitable):
    print("acquire")
    for future in coro.__await__():
        print("release")
        await AwaitOne(future)
        print("acquire")
    print("release")

我认为这里发生的事情是新的

await
语法模拟的不是旧式
yield
而是旧式
yield from
。现在我们需要从可迭代的未来中产生收益,但我们只有一个未来。因此,让我们将其包装在长度为 1 的序列的
await
中。这个长度为一的容器只需要一个
__await__
方法来产生它的单一未来。

© www.soinside.com 2019 - 2024. All rights reserved.