Прежде всего, по умолчанию в вашем примере контекст не будет переключаться.Другими словами, пока сопрограмма не столкнется с чем-то, что фактически блокирует (например, Future
), она не вернет управление циклу событий и не возобновит свой путь непосредственно к внутренней сопрограмме.
Я не знаю более простого способа продемонстрироватьэто чем наследование реализации цикла событий по умолчанию:
import asyncio
class TestEventLoop(asyncio.SelectorEventLoop):
def _run_once(self):
print('control inside event loop')
super()._run_once()
async def func1():
await asyncio.sleep(1)
async def func2():
print('before func1')
await func1()
print('after func1')
async def main():
print('before func2')
await func2()
print('after func2')
loop = TestEventLoop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(main())
finally:
loop.close()
В выводе вы увидите:
control inside event loop
before func2
before func1
control inside event loop
control inside event loop
after func1
after func2
control inside event loop
func2
передал поток выполнения непосредственно в func1
, избегая _run_once
цикла обработкиэто может переключиться на другую сопрограмму.Только при блокировке asyncio.sleep
цикл событий получил контроль.
Хотя это деталь реализации цикла событий по умолчанию.
Во-вторых, и это, вероятно,Гораздо важнее, что переключение между сопрограммами обходится очень дешево по сравнению с тем преимуществом, которое мы получаем от использования asyncio для работы с вводом / выводом.
Это также намного дешевле, чем другие альтернативы асинхронности, такие как переключение между потоками ОС.
Ситуация, когда ваш код работает медленно из-за множества сопрограмм, крайне маловероятна, но даже если это произошло, вам, вероятно, стоит взглянуть на более эффективные реализации цикла событий, такие как uvloop .