来自协程的产量与来自任务的产量

Gil*_*tes 23 python concurrency asynchronous python-3.x python-asyncio

Guido van Rossum在2014年关于Tulip/Asyncio的演讲中展示了幻灯片:

任务与协同程序

  • 相比:

    • res =来自some_coroutine的收益率(...)
    • res =来自任务的产量(some_coroutine(...))
  • 任务可以在不等待的情况下取得进展

    • 作为日志,你等待别的东西
      • 即产量

我完全忽略了这一点.

从我的观点来看,两个结构都是相同的:

如果是裸协程 - 它会被调度,所以无论如何都会创建任务,因为调度程序与Tasks一起运行,然后协程调用程序协同程序被暂停,直到被调用者完成,然后可以自由继续执行.

如果Task- 完全相同 - 新任务被调度,调用者协程等待完成.

在这两种情况下执行代码的方式与开发人员在实践中应考虑的影响有何不同?

ps
非常感谢与权威来源(GvR,PEP,docs,核心开发者笔记)的链接.

And*_*lov 27

对于主叫方而言,co-routine yield from coroutine()感觉就像一个函数调用(即当coroutine()完成时它将再次获得控制权).

yield from Task(coroutine())另一方面,感觉更像是创建一个新线程.Task()几乎立即返回,并且调用者很可能在coroutine()完成之前获得控制权.

f()和之间的区别th = threading.Thread(target=f, args=()); th.start(); th.join()是显而易见的吧?

  • 根本没有asyncio的优先级.对于裸协同程序,你必须使用`coro()`来运行coroutine,如果像`async(coro())这样的任务构造`将与其他程序并行执行协同程序. (2认同)

dan*_*ano 13

采用的点asyncio.Task(coro())是,你的情况希望明确等待coro,但你要coro在后台执行,而你等待其他任务.这就是Guido的幻灯片意味着什么

[A] Task可以在不等待的情况下取得进展...... 只要你等待别的东西

考虑这个例子:

import asyncio

@asyncio.coroutine
def test1():
    print("in test1")


@asyncio.coroutine
def dummy():
    yield from asyncio.sleep(1)
    print("dummy ran")


@asyncio.coroutine
def main():
    test1()
    yield from dummy()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)

输出:

dummy ran
Run Code Online (Sandbox Code Playgroud)

如您所见,test1从未实际执行过,因为我们没有明确地调用yield from它.

现在,如果我们使用asyncio.async包装Task实例test1,结果是不同的:

import asyncio

@asyncio.coroutine
def test1():
    print("in test1")


@asyncio.coroutine
def dummy():
    yield from asyncio.sleep(1)
    print("dummy ran")


@asyncio.coroutine
def main():
    asyncio.async(test1())
    yield from dummy()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)

输出:

in test1
dummy ran
Run Code Online (Sandbox Code Playgroud)

因此,实际上没有实际的使用理由yield from asyncio.async(coro()),因为它比yield from coro()没有任何好处更慢; 它引入了添加coro到内部asyncio调度程序的开销,但这不是必需的,因为无论如何都要使用要执行的yield from保证coro.如果你只是想调用一个协同程序并等待它完成,只需yield from直接调用协程.

边注:

我正在使用asyncio.async*而不是Task直接因为文档推荐它:

不要直接创建Task实例:使用async()函数或BaseEventLoop.create_task()方法.

*请注意,从Python 3.4.4开始,asyncio.async不赞成使用asyncio.ensure_future.