Guido van Rossum,在2014年关于郁金香/异步的演讲中:
Tasks vs coroutines
Compare:
- res = yield from some_coroutine(...)
- res = yield from Task(some_coroutine(...))
Task can make progress without waiting for it
- As log as you wait for something else
- i.e. yield from
我完全没有抓住要点。
在我看来,这两种结构是相同的:
在裸机协程的情况下-它被调度,所以任务无论如何都会被创建,因为调度器对任务进行操作,然后协程调用方协程被挂起,直到被调用方完成,然后变得可以继续执行。
在Task
的情况下,所有相同的新任务都被调度,调用方协程等待其完成。
在这两种情况下,代码的执行方式有什么不同?开发人员在实践中应该考虑它会产生什么影响?
p.s.
链接到权威来源(GvR,pep,docs,核心devs注释)将非常感谢。
使用
asyncio.Task(coro())
的要点是,对于那些不希望显式等待coro
,但希望在等待其他任务时在后台执行coro
的情况。这就是Guido的幻灯片的意思举个例子:
输出:
如您所见,
test1
实际上从未执行过,因为我们没有显式地对它调用yield from
。现在,如果我们使用
asyncio.async
将一个Task
实例包装在test1
上,结果是不同的:输出:
因此,使用
yield from asyncio.async(coro())
实际上没有任何实际的原因,因为它比yield from coro()
慢,没有任何好处;它引入了向内部asyncio
调度程序添加coro
的开销,但这不是必需的,因为无论如何,使用yield from
可以保证coro
将执行。如果您只想调用协程并等待它完成,那么直接调用协程。旁注:
我正在使用
asyncio.async
*而不是Task
直接使用because the docs recommend it:*请注意,从Python 3.4.4开始,} 。
asyncio.async
已被弃用,取而代之的是^{因为调用端的co例程
yield from coroutine()
感觉像一个函数调用(即,当co routine()完成时,它将再次获得控制权)。另一方面,感觉更像是创建了一个新线程。
Task()
几乎立即返回,而且很可能在coroutine()
完成之前,调用者获得了控制权。f()
和th = threading.Thread(target=f, args=()); th.start(); th.join()
之间的差别很明显,对吧?如PEP 380中所述,引入的已接受PEP文档产生于表达式
res = yield from f()
,它来自以下循环的思想:这样,事情就变得非常清楚:如果
f()
是some_coroutine()
,那么就执行协程。另一方面,如果f()
是Task(some_coroutine())
,则执行Task.__init__
。some_coroutine()
未执行,只有新创建的生成器作为第一个参数传递给Task.__init__
。结论:
res = yield from some_coroutine()
=>;协同程序继续执行并返回下一个值res = yield from Task(some_coroutine())
=>;将创建一个新任务,其中存储一个未执行的some_coroutine()
生成器对象。相关问题 更多 >
编程相关推荐