简体   繁体   English

Python协同程序

[英]Python coroutines

I have a little bit of experience with promises in Javascript. 我对Javascript中的promises有一点经验。 I am quite experienced with Python, but new to its coroutines, and there is a bit that I just fail to understand: where does the asynchronicity kick in? 我对Python非常有经验,但对于它的协同程序来说是新手,有一点我不明白:异步性在哪里开始?

Let's consider the following minimal example: 让我们考虑以下最小的例子:

async def gen():
    await something
    return 42

As I understand it, await something puts execution of our function aside and lets the main program run other bits. 据我了解, await something函数的执行放在一边,让主程序运行其他位。 At some point something has a new result and gen will have a result soon after. 在某些时候something会产生新的结果,并且gen很快就会产生结果。

If gen and something are coroutines, then by all internet wisdom they are generators. 如果gensomething是协同程序,那么通过所有的互联网智慧,它们就是生成器。 And the only way to know when a generator has a new item available, afaik, is by polling it: x=gen(); next(x) 通过轮询来了解生成器何时可用新项目的唯一方法是: x=gen(); next(x) x=gen(); next(x) . x=gen(); next(x) But this is blocking! 但这是阻止! How does the scheduler "know" when x has a result? x有结果时,调度程序如何“知道”? The answer can't be "when something has a result" because something must be a generator, too (for it is a coroutine). 答案不能是“ something有结果”,因为something必须是生成器(因为它是一个协程)。 And this argument applies recursively. 这个论点递归地适用。

I can't get past this idea that at some point the process will just have to sit and wait synchronously. 我无法理解这个想法,在某些时候,这个过程只需要坐下来等待。

The secret sauce here is the asyncio module . 这里的秘诀是asyncio模块 Your something object has to be an awaitable object itself, and either depend on more awaitable objects, or must yield from a Future object . 你的something对象本身必须是一个等待对象,要么依赖于更多等待的对象,要么必须从Future对象中产生

For example, the asyncio.sleep() coroutine yields a Future : 例如, asyncio.sleep()asyncio.sleep()一个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()

(The syntax here uses the older generator syntax, to remain backwards compatible with older Python 3 releases). (此处的语法使用较旧的生成器语法,以保持向后兼容较旧的Python 3版本)。

Note that a future doesn't use await or yield from ; 请注意,未来不使用awaityield from ; they simply use yield self until some condition is met . 他们只是使用yield self直到满足某些条件 In the above async.sleep() coroutine, that condition is met when a result has been produced (in the async.sleep() code above, via the futures._set_result_unless_cancelled() function called after a delay). 在上面的async.sleep()协同程序中,当产生结果时(在上面的async.sleep()代码中,通过延迟后调用的async.sleep() futures._set_result_unless_cancelled()函数async.sleep()满足该条件。

An event loop then keeps pulling in the next 'result' from each pending future it manages (polling them efficiently) until the future signals it is done (by raising a StopIteration exception holding the results; return from a co-routine would do that, for example). 然后事件循环继续从它管理的每个待处理的未来(有效地轮询它们)中拉入下一个“结果”,直到将来发出信号(通过引发保持结果的StopIteration异常;从协同例程return将执行此操作,例如)。 At that point the coroutine that yielded the future can be signalled to continue (either by sending the future result, or by throwing an exception if the future raised anything other than StopIteration ). 在那时,产生未来的协程可以发出信号继续(通过发送未来结果,或者如果未来提出除StopIteration以外的任何东西则抛出异常)。

So for your example, the loop will kick off your gen() coroutine, and await something then (directly or indirectly) yields a future. 因此,对于您的示例,循环将启动您的gen()协程,然后await something (直接或间接)产生未来。 That future is polled until it raises StopIteration (signalling it is done) or raises some other exception. 对该未来进行轮询,直到它引发StopIteration (发出信号)或引发其他一些异常。 If the future is done, coroutine.send(result) is executed, allowing it to then advance to the return 42 line, triggering a new StopIteration exception with that value, allowing a calling coroutine awaiting on gen() to continue, etc. 如果未来完成,则执行coroutine.send(result) ,然后允许它前进到return 42行,触发具有该值的新StopIteration异常,允许等待gen()的调用协程继续等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM