Как упомянуто в комментариях, обычно сопрограммы asyncio автоматически приостанавливают вызовы, которые блокируют или спят в эквивалентном синхронном кофе.В вашем случае сопрограмма привязана к процессору, поэтому ожидания блокирующих вызовов недостаточно, необходимо время от времени передавать управление циклу событий, чтобы разрешить запуск остальной системы.
Явные выходы нередки всовместная многозадачность, и использование await asyncio.sleep(0)
для этой цели будет работать как задумано , это несет в себе риск: слишком часто спите, и вы замедляете вычисления из-за ненужных переключателей;спите слишком редко, и вы замыкаете цикл обработки событий, проводя слишком много времени в одной сопрограмме.
Решение, предлагаемое asyncio, состоит в том, чтобы разгрузить код, связанный с ЦП, в пул потоков, используя run_in_executor
.Ожидание этого автоматически приостановит сопрограмму, пока задача интенсивного использования процессора не будет сделана, без какого-либо промежуточного опроса.Например:
import asyncio
def do(id, amount):
for i in range(amount):
# Do some time-expensive work
print(f'{id}: has done {i}')
return f'{id}: done'
async def main():
loop = asyncio.get_event_loop()
res = await asyncio.gather(
loop.run_in_executor(None, do, 'Task1', 5),
loop.run_in_executor(None, do, 'Task2', 3))
print(*res, sep='\n')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())