Python3.5中协同程序和未来/任务之间的区别?
假设我们有一个虚拟函数:Python3.5中协同程序和未来/任务之间的区别?,python,python-3.x,python-asyncio,Python,Python 3.x,Python Asyncio,假设我们有一个虚拟函数: async def foo(arg): result = await some_remote_call(arg) return result.upper() 这两者之间的区别是什么: import asyncio coros = [] for i in range(5): coros.append(foo(i)) loop = asyncio.get_event_loop() loop.run_until_complete(asyn
async def foo(arg):
result = await some_remote_call(arg)
return result.upper()
这两者之间的区别是什么:
import asyncio
coros = []
for i in range(5):
coros.append(foo(i))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(coros))
以及:
注意:该示例返回一个结果,但这不是问题的重点。如果返回值很重要,请使用gather()
而不是wait()
不管返回值如何,我都希望sure\u future()
更清晰wait(coros)
和wait(futures)
都运行协同程序,那么何时以及为什么要在中包装协同程序以确保未来
基本上,使用Python3.5的async
运行一系列非阻塞操作的正确方法(tm)是什么
为了获得额外的积分,如果我想成批打电话怎么办?例如,我需要调用一些远程调用(…)
1000次,但我不想用1000个同时连接破坏web服务器/数据库/etc。这在线程或进程池中是可行的,但是有没有办法在asyncio
中实现这一点
2020更新(Python 3.7+):不要使用这些代码段。而是使用:
import asyncio
async def do_something_async():
tasks = []
for i in range(5):
tasks.append(asyncio.create_task(foo(i)))
await asyncio.gather(*tasks)
def do_something():
asyncio.run(do_something_async)
还考虑使用AssiCIO的一个强大的第三方替代。
< P>一个协同程序是一个生成器函数,它既可以产生值,也可以从外部接受值。使用协同程序的好处是,我们可以暂停函数的执行,稍后再继续执行。在网络操作的情况下,在等待响应时暂停函数的执行是有意义的。我们可以利用这段时间运行其他一些函数 未来就像Javascript中的Promise
对象。它就像一个占位符,用于表示将来将具体化的值。在上述情况下,当等待网络I/O时,函数可以给我们一个容器,一个承诺,当操作完成时,它将用值填充容器。我们保留未来的对象,当它实现时,我们可以调用它的方法来检索实际结果
直接回答:如果您不需要结果,您不需要确保未来。如果需要结果或检索发生的异常,它们是很好的
额外积分:我会选择并传递一个Executor
实例来控制最大工作人数
解释和示例代码
在第一个示例中,您使用的是协同程序。wait
函数接受一组协程并将它们组合在一起。所以wait()
run_until_complete
方法将确保循环在执行完成之前处于活动状态。请注意,在这种情况下,您如何无法获得异步执行的结果
在第二个示例中,您使用sure\u future
函数包装一个协同程序,并返回一个Task
对象,它是一种future
。当您调用sure\u future
时,协程计划在主事件循环中执行。返回的future/task对象还没有值,但随着时间的推移,当网络操作完成时,future对象将保存操作的结果
from asyncio import ensure_future
futures = []
for i in range(5):
futures.append(ensure_future(foo(i)))
loop = get_event_loop()
loop.run_until_complete(wait(futures))
所以在这个例子中,我们做了同样的事情,除了我们使用未来,而不是仅仅使用协同程序
让我们看一个如何使用asyncio/coroutines/futures的示例:
import asyncio
async def slow_operation():
await asyncio.sleep(1)
return 'Future is done!'
def got_result(future):
print(future.result())
# We have result, so let's stop
loop.stop()
loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)
# We run forever
loop.run_forever()
这里,我们在循环
对象上使用了create_task
方法<代码>确保未来将在主事件循环中安排任务。这种方法使我们能够在所选择的循环上调度一个协同路由
我们还看到了在task对象上使用add\u done\u callback
方法添加回调的概念
当协同程序返回值、引发异常或被取消时,任务
即完成
。有一些方法可以检查这些事件
我写了一些关于这些主题的博客文章,可能会有所帮助:
当然,您可以在官方手册上找到更多详细信息:Vincent的评论链接到,这表明wait()
为您在sure_future()
中包装了协同程序
换句话说,我们确实需要一个未来,而协同程序将被默默地转化为它们
当我找到关于如何批处理协同计划/期货的明确解释时,我将更新此答案。
任务
- 这是一个包裹在未来的合作项目
- 类任务是类Future的一个子类
- 因此它也适用于等待李>
- 它与裸协同程序有何不同
- 它可以在不等待的情况下取得进展
- 只要你在等别的东西,比如说。
- 等待[其他东西]
考虑到这一点,确保未来作为创建任务的名称是有意义的,因为无论您是否等待它(只要您等待某事),都将计算未来的结果。这允许事件循环在您等待其他事情时完成您的任务。请注意,在Python 3.7中,创建任务是首选方法
注:我将Guido幻灯片中的“收益率”改为“等待”,以体现现代性。简单答案
- 调用协程函数(
async def
)不会运行它。它返回一个协同程序对象,就像生成器函数返回生成器对象一样
wait
从协程中检索值,即“调用”协程
eusure\u future/create\u task
计划在下一次迭代时在事件循环上运行协同程序(尽管不像守护进程线程那样等待它们完成)
一些代码示例
让我们首先明确一些术语:
- 协同程序函数,您可以
async def
s李>
- 科罗
from asyncio import ensure_future
futures = []
for i in range(5):
futures.append(ensure_future(foo(i)))
loop = get_event_loop()
loop.run_until_complete(wait(futures))
import asyncio
async def slow_operation():
await asyncio.sleep(1)
return 'Future is done!'
def got_result(future):
print(future.result())
# We have result, so let's stop
loop.stop()
loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)
# We run forever
loop.run_forever()
import asyncio
import time
# coroutine function
async def p(word):
print(f'{time.time()} - {word}')
async def main():
loop = asyncio.get_event_loop()
coro = p('await') # coroutine
task2 = loop.create_task(p('create_task')) # <- runs in next iteration
await coro # <-- run directly
await task2
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
1539486251.7055213 - await
1539486251.7055705 - create_task
async def main():
loop = asyncio.get_event_loop()
coro = p('await')
task2 = loop.create_task(p('create_task')) # scheduled to next iteration
await asyncio.sleep(1) # loop got control, and runs task2
await coro # run coro
await task2
-> % python coro.py
1539486378.5244057 - create_task
1539486379.5252144 - await # note the delay