Python协同程序

时间:2017-03-06 10:42:15

标签: python asynchronous async-await

我对Javascript中的承诺有一点经验。我对Python非常有经验,但对于它的协同程序来说是新手,有一点我无法理解:异步性在哪里开始?

让我们考虑以下最小例子:

async def gen():
    await something
    return 42

据我所知,await something将我们的函数执行放在一边,让主程序运行其他位。在某些时候something会有一个新结果,gen很快就会有结果。

如果gensomething是协程,那么通过所有互联网智慧,它们都是生成器。通过轮询来了解生成器何时可用新项目的唯一方法是:x=gen(); next(x)。但这是阻止!当x有结果时,调度程序如何“知道”?答案不能是“当something有结果时”,因为something也必须是生成器(因为它是一个协程)。这个论点递归地适用。

我无法理解这个想法,在某些时候,这个过程只需要坐下来等待。

1 个答案:

答案 0 :(得分:6)

这里的秘诀是asyncio module。你的something对象本身必须是一个等待的对象,要么依赖于更多等待的对象,要么必须从Future object得到。

例如,asyncio.sleep() coroutine会产生Future

@coroutine
def sleep(delay, result=None, *, loop=None):
    """Coroutine that completes after a given time (in seconds)."""
    if delay == 0:
        yield
        return result

    if loop is None:
        loop = events.get_event_loop()
    future = loop.create_future()
    h = future._loop.call_later(delay,
                                futures._set_result_unless_cancelled,
                                future, result)
    try:
        return (yield from future)
    finally:
        h.cancel()

(此处的语法使用较旧的生成器语法,以保持向后兼容较旧的Python 3版本。)

请注意,未来不会使用awaityield from;他们只使用yield self until some condition is met。在上面的async.sleep()协程中,在生成结果时(在上面的async.sleep()代码中,通过延迟后调用的futures._set_result_unless_cancelled()函数)满足该条件。

事件循环然后继续从它管理的每个待处理的未来(有效地轮询它们)中拉入下一个“结果”,直到将来发出信号(通过引发保持结果的StopIteration异常; {{1例如,从一个协同例程就可以做到这一点。在那时,产生未来的协程可以继续发出信号(通过发送未来结果,或者如果未来提出除return以外的任何内容,则抛出异常)。

因此,对于您的示例,循环将启动您的StopIteration协程,然后gen()(直接或间接)产生未来。对该未来进行轮询,直到它引发await something(发出信号)或引发其他一些异常。如果未来完成,则执行StopIteration,然后允许它前进到coroutine.send(result)行,触发带有该值的新return 42异常,允许等待{{1}的呼叫协程继续等等。