【问题标题】:How can I use 'yield' statement in python 3.8 's asynchronous programming?在 python 3.8 的异步编程中如何使用 'yield' 语句?
【发布时间】:2020-06-21 11:49:57
【问题描述】:

在python的asyncio异步编程(3.7或以下版本)中,如果我想手动让协程将其控制权交还给主事件循环,我可以使用这段代码:

@asyncio.coroutine
def switch():
    yield
    return

async def task():
    # ...do something
    # ...
    await switch() # then this coroutine will be suspended and other will be triggered
    # ...
    # ... do something else when it's triggered again.

但是在 python3.8 中,“@coroutine”装饰器已被弃用。此外,我不能在“async def”中使用yield(因为它将定义一个异步生成器而不是协程)。那么我怎样才能实现同样的功能呢?

【问题讨论】:

  • 有什么理由要实现自己的switch协程吗?实际上,所有事件循环都考虑它们各自的sleep(0) 来执行此操作。最重要的是,并非每个事件循环都会正确响应空的yield
  • @asyncio.coroutine 已被弃用,但 @types.coroutine 不是(也不会是),所以你应该使用它。如果你仔细看,asyncio.sleep 在内部也使用了那个。

标签: python python-asyncio


【解决方案1】:

TLDR:不要使用显式的yield 来切换协程。对于asyncio,请改用asyncio.sleep(0)


实际上,所有事件循环都认为它们各自的sleep 持续时间为 0 表示“让其他协程运行”。 对于asyncio,使用asyncio.sleep(0) 让其他协程运行。

async def task():
    # ...do something
    # ...
    await asyncio.sleep(0) # then this coroutine will be suspended and other will be triggered
    # ...
    # ... do something else when it's triggered again.

Sleeping (asyncio)

sleep() 总是暂停当前任务,允许其他任务运行。

Checkpoints (trio)

...知道await trio.sleep(0) 是一种无需执行任何其他操作即可执行检查点的惯用方式很有用...

Time (curio)

睡眠指定的秒数。如果秒数为 0,则执行切换到下一个就绪任务(如果有)。


如果由于某种原因需要显式的 yield 指向事件循环,请在其 __await__ 特殊方法中创建一个自定义类和 yield

class Switch:
    """Switch coroutines by yielding to the event loop"""
    def __await__(self):
        yield

请注意,这会将None 发送到事件循环。事件循环是否以及如何处理此信号取决于所使用的异步库。

【讨论】:

  • 谢谢兄弟,使用__await__方法可以实现同样的功能。我不喜欢asyncio.sleep(0)的原因是它比自定义切换器慢10%......
  • 我查看了 py3.8 的 asyncio.sleep() 的源代码实现,它使用了 @types.coroutine 装饰器。使用它而不是 asyncio.sleep(0) 可以在我的电脑上加速大约 12%。
  • async应该在async def __await__(self):的行中省略
  • @wangsquirrel 你说得对,感谢您发现这一点。现已修复。
猜你喜欢
  • 1970-01-01
  • 2019-05-10
  • 2016-09-29
  • 2019-01-22
  • 2020-04-07
  • 2021-02-08
  • 1970-01-01
  • 2019-03-02
  • 2017-08-27
相关资源
最近更新 更多