协程产量vs助产剂产量

2024-06-01 02:54:56 发布

您现在位置:Python中文网/ 问答频道 /正文

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注释)将非常感谢。


Tags: fromfortask情况ressome调度van
3条回答

使用asyncio.Task(coro())的要点是,对于那些不希望显式等待coro,但希望在等待其他任务时在后台执行coro的情况。这就是Guido的幻灯片的意思

[A] Task can make progress without waiting for it...as long as you wait for something else

举个例子:

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())

输出:

dummy ran

如您所见,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())

输出:

in test1
dummy ran

因此,使用yield from asyncio.async(coro())实际上没有任何实际的原因,因为它比yield from coro()慢,没有任何好处;它引入了向内部asyncio调度程序添加coro的开销,但这不是必需的,因为无论如何,使用yield from可以保证coro将执行。如果您只想调用协程并等待它完成,那么直接调用协程。

旁注:

我正在使用asyncio.async*而不是Task直接使用because the docs recommend it

Don’t directly create Task instances: use the async() function or the BaseEventLoop.create_task() method.

*请注意,从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(),它来自以下循环的思想:

for res in f():
    yield res

这样,事情就变得非常清楚:如果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()生成器对象。

相关问题 更多 >