【问题标题】:Call a coroutine without yielding the event loop调用协程而不产生事件循环
【发布时间】:2019-08-28 14:27:05
【问题描述】:

出于可读性原因,我可能会分解代码。所以

async coro_top():
  print('top')
  print('1')
  # ... More asyncio code

  print('2')
  # ... More asyncio code

...变成类似

async coro_top():
  print('top')
  await coro_1()
  await coro_2()

async coro_1()
  print('1')
  # ... More asyncio code

async coro_2()
  print('2')
  # ... More asyncio code

但是,额外的awaits 意味着它们不是严格等价的

  • 另一个并发任务可以在print('top')print('1') 之间运行代码,因此对于某些算法来说,竞争条件更有可能发生。

  • 产生事件循环有(大概)一点点开销

那么有没有办法在不产生事件循环的情况下调用协程来避免上述情况?

【问题讨论】:

标签: python python-3.x python-asyncio


【解决方案1】:

问题的前提不正确:与人们的预期相反,await 不会自动屈服于事件循环。您可以轻松地对其进行测试:

async def noop():
    pass

async def busy_loop(msg):
    while True:
        print(msg)
        await noop()

# keeps printing 'a', the event loop is stuck
asyncio.get_event_loop().run_until_complete(
    asyncio.gather(busy_loop('a'), busy_loop('b')))

busy_loop虽然一直在等待,但还是阻塞了事件循环,让其他任务无法运行,甚至取消也是不可能的。这是因为它等待的 noop 协程永远不会暂停执行。

await some_coroutine() 并不意味着“安排some_coroutine() 并让步到事件循环,完成后恢复”。它的意思是“开始执行some_coroutine(),并且,如果/当它选择暂停时,继续暂停”,并假设前者可以leadbugs

也就是说,分解后的代码真的等同于重构前的代码。在print('top')print('1') 之间执行另一个任务的唯一方法是在它们之间添加一个新的await(实际上挂起协程),但对于原始代码也可以这样说。

产生事件循环有(大概)轻微的开销

存在开销,但它与函数调用的开销相当,而不是运行事件循环迭代的显着更大的开销。

【讨论】:

  • 谢谢! (我用 asyncio Python 编程已经有一段时间了,我有点尴尬,我没有像我想象的那样理解这个......)
猜你喜欢
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 2019-08-06
  • 2015-04-22
  • 2020-02-26
  • 1970-01-01
  • 2023-02-11
  • 2023-03-19
相关资源
最近更新 更多