Как работает вариабельность с задачами в asyncio?

#python-asyncio

Вопрос:

Во время изучения asyncio я допустил ошибку и трижды сделал ссылку на переменную «task1».

 import asyncio import time  async def say_after(delay, what):  await asyncio.sleep(delay)  print(what)  async def main():  print(f"start at {time.strftime('%X')}")  task1 = asyncio.create_task(say_after(1, 'a'))  task1 = asyncio.create_task(say_after(2, 'aba'))  task1 = asyncio.create_task(say_after(3, 'faf'))   await task1  #await task2  print(f"finished at {time.strftime('%X')}")  asyncio.run(main())  

Но когда я запустил код, я получил этот вывод

 start at 19:05:19  a  aba  faf  finished at 19:05:22  

Почему он не печатает только последнюю созданную задачу? Почему первое и второе задания не очищаются?

На понятном python, если я напишу что-то вроде этого:

 page = 0 page = 1 page = 2  print(page)  

Я получу «2», потому что предыдущие ссылки на объекты будут удалены из памяти.

Почему там все по-другому? Как это работает?

Ответ №1:

Вы начинаете выполнять три задачи и ждете, пока завершится самая медленная из трех.

await task1 не выполняется задача, она ожидает завершения третьего объекта задачи, назначенного для task1 завершения. Две другие задачи все еще существуют в цикле событий и все еще выполняются, они заканчиваются первыми, потому что они быстрее.

Другой способ взглянуть на это — немного изменить свой код. Это сделает то, с чего вы ожидаете начать, а затем выдаст ошибки, потому что программа завершится раньше более медленных задач.

 task1 = asyncio.create_task(say_after(3, 'a')) # make slowest  task1 = asyncio.create_task(say_after(2, 'aba'))  task1 = asyncio.create_task(say_after(1, 'faf')) # make fastest  

Переписывание кода task1= -это полное удаление, чтобы было ясно, что await task1 это больше связано с ожиданием завершения задачи и мало связано с ее запуском.

 asyncio.create_task(say_after(1, 'a'))  asyncio.create_task(say_after(2, 'aba'))  asyncio.create_task(say_after(3, 'faf'))   while len(asyncio.all_tasks()) gt; 1:  await asyncio.sleep(0.1)  # await task1  

Более обычный способ — собрать ссылки на все задачи и дождаться завершения списка задач.

 tasks = []  tasks.append(asyncio.create_task(say_after(3, 'a')))  tasks.append(asyncio.create_task(say_after(2, 'aba')))  tasks.append(asyncio.create_task(say_after(1, 'faf')))   await asyncio.gather(*tasks)