Python asyncio ensure_future装饰器

问题描述 投票:3回答:2

我们假设我是asyncio的新手。我正在使用async / await来并行化我当前的项目,并且我发现自己将所有的协程传递给asyncio.ensure_future。很多像这样的东西:

coroutine = my_async_fn(*args, **kwargs)
task = asyncio.ensure_future(coroutine)

我真正喜欢的是调用异步函数来返回执行任务而不是空闲协程。我创建了一个装饰器来完成我想要做的事情。

def make_task(fn):
    def wrapper(*args, **kwargs):
        return asyncio.ensure_future(fn(*args, **kwargs))
    return wrapper

@make_task
async def my_async_func(*args, **kwargs):
    # usually making a request of some sort
    pass

asyncio是否有内置的方法来执行此操作我无法找到?如果我开始导致这个问题,我使用asyncio是错误的吗?

python python-3.x python-3.6 python-asyncio
2个回答
2
投票

asyncio是否有内置的方法来执行此操作我无法找到?

不,asyncio没有装饰器将协同函数转换为任务。

如果我开始导致这个问题,我使用asyncio是错误的吗?

没有看到你在做什么就很难说,但我认为它可能恰好是真的。虽然创建任务是asyncio程序中的常规操作,但我怀疑你创建了这么多的协同程序,它们应该始终是任务。

等待协同程序 - 是一种“异步调用某些函数”的方法,但阻止当前执行流程直到完成:

await some()

# you'll reach this line *only* when some() done 

另一方面任务 - 是一种“运行函数in background”的方法,它不会阻止当前的执行流程:

task = asyncio.ensure_future(some())

# you'll reach this line immediately

当我们编写asyncio程序时,我们通常需要第一种方法,因为在开始下一个程序之前我们通常需要一些操作的结果:

text = await request(url)

links = parse_links(text)  # we need to reach this line only when we got 'text'

另一方面,创建任务通常意味着遵循进一步的代码不依赖于任务的结果。但同样,它并不总是发生。

由于qazxsw poi立即返回,有些人尝试使用它作为同时运行一些协同程序的方法:

ensure_future

正确的方法是使用# wrong way to run concurrently: asyncio.ensure_future(request(url1)) asyncio.ensure_future(request(url2)) asyncio.ensure_future(request(url3))

asyncio.gather

可能这是你想要的吗?

UPD:

我认为在你的情况下使用任务是个好主意。但我不认为你应该使用装饰器:协程功能(发出请求)仍然是它的具体使用细节(它将用作任务)的一个单独部分。如果请求同步控制与其主要功能分开,那么将同步移动到单独的功能中也是有意义的。我会做这样的事情:

# correct way to run concurrently:
await asyncio.gather(
    request(url1),
    request(url2),
    request(url3),
)

2
投票

asyncio在很早发布的版本中有import asyncio async def request(i): print(f'{i} started') await asyncio.sleep(i) print(f'{i} finished') return i async def when_ready(conditions, coro_to_start): await asyncio.gather(*conditions, return_exceptions=True) return await coro_to_start async def main(): t = asyncio.ensure_future t1 = t(request(1)) t2 = t(request(2)) t3 = t(request(3)) t4 = t(when_ready([t1, t2], request(4))) t5 = t(when_ready([t2, t3], request(5))) await asyncio.gather(t1, t2, t3, t4, t5) if __name__ == '__main__': loop = asyncio.get_event_loop() try: loop.run_until_complete(main()) finally: loop.run_until_complete(loop.shutdown_asyncgens()) loop.close() 装饰器,但是我们删除了它。

原因是装饰器不知道要使用什么循环。 asyncio不会在导入时实例化循环,而且为了测试隔离,测试套件通常会为每个测试创建一个新循环。

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