【问题标题】:Python coroutinesPython 协程
【发布时间】:2017-07-26 04:42:41
【问题描述】:

我对 Javascript 中的 Promise 有一点经验。我对 Python 非常有经验,但对它的协程很陌生,而且有一点我无法理解:异步性在哪里起作用?

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

async def gen():
    await something
    return 42

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

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

我无法摆脱这样的想法,即在某些时候该过程只需要同步等待。

【问题讨论】:

    标签: python asynchronous async-await


    【解决方案1】:

    这里的秘诀是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 异常来保存结果;return 从例如,协同程序会这样做)。此时,可以向产生未来的协程发出信号以继续(通过发送未来结果,或者如果未来引发了StopIteration 以外的任何内容,则抛出异常)。

    因此,对于您的示例,循环将启动您的 gen() 协程,然后 await something(直接或间接)产生一个未来。轮询该未来,直到它引发StopIteration(表示它已完成)或引发其他一些异常。如果未来完成,coroutine.send(result) 将被执行,允许它前进到return 42 行,触发具有该值的新StopIteration 异常,允许在gen() 上等待的调用协程继续,等等。

    【讨论】:

    • 那么你的意思是,协程并不总是生成器,而是可以产生Future(假设我们选择使用asyncio)?而这个Future 是递归结束时的那个,它知道如何绑定到异步框架?那么生成器协程其实根本就不是异步的?
    • @Paul:协程只是知道如何处理抛出异常的生成器,真的。语法更好。始终由开发人员来确保协程是真正合作的,并允许自己在异步上下文中使用。如果您编写的 async def 协程只是阻塞,那是您自己的错。 :-)
    • @Paul:这适用于任何语言的任何协程框架。 Java 没有asyncawait 语法,但有Futures。 C# 和 JS 的协程特性是一样的,还有基于futures和event loop的异步协作的整体前提。
    • 我的误解是我认为asyncio是负责在协程需要等待时不阻塞的人,但这实际上是协程的责任。我也没有意识到协程最终需要如何回到事件循环以保持异步。我一直认为示例代码中的asyncio.sleep 行纯粹是出于演示目的,但忽略了与事件循环实际结合的重要性。现在我明白了。我想。
    • @Paul: 请记住,coroutine 中的 co 代表 cooperative。协程最终负责与其他协程协作。与其随意切换(像线程那样),不如在适合协程的地方切换。
    猜你喜欢
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 2017-06-15
    • 2018-03-06
    • 2020-12-29
    • 2017-09-02
    相关资源
    最近更新 更多