调用协程而不产生事件循环

时间:2019-04-07 07:07:55

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

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

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

但是,额外的await表示它们不是严格等价的

  • 另一个并发任务可以在print('top')print('1')之间运行代码,因此对于某些算法,竞态条件更容易出现。

  • (大概)产生事件循环的开销很小

为了避免上述情况,有没有一种方法可以在不产生事件循环的情况下调用协程?

1 个答案:

答案 0 :(得分:3)

这个问题的前提是不正确的:与人们的期望相反,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(实际上是暂停协程)。原始代码也是如此。

  

(大概)产生事件循环的开销很小

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