import asyncio asyncio.create_task(coro, *, name=None, context=None)
coro
- асинхронная функция coroutine
,name=None
- имя задачи.context=None
- пользовательский contextvars.Context
для запуска сопрограммы.asyncio.Task
.Функция create_task()
модуля asyncio
оборачивает сопрограмму coro
в задачу task
и планирует ее выполнение в ближайшее время. Возвращает объект Task
. Объект задачи всегда следует запускать с оператором await
.
Если аргумент name
не равен None
, то name
устанавливается как имя задачи с помощью метода объекта Task.set_name()
.
Необязательный ключевой аргумент контекста context
, позволяет указать пользовательский contextvars.Context
для запуска сопрограммы. Текущая копия контекста создается, когда контекст не предоставляется.
Задача будет выполняется в цикле событий, который возвращает функция низкоуровнего API asyncio.get_running_loop()
. Если в текущем потоке нет запущенного цикла событий, то возникает исключение RuntimeError
.
Обратите внимание, что
asyncio.TaskGroup.create_task()
(добавлена в Python3.11) - это более новая альтернатива, которая позволяет удобно ожидать группу связанных задач.
Важно! Сохраняйте ссылку на результат этой функции, чтобы задача не исчезла в процессе выполнения. Цикл событий сохраняет только слабые ссылки на задачи. Задача, на которую больше нигде нет ссылок, может быть удалена сборщиком мусора в любое время, даже до того, как она будет выполнена. Для надежных фоновых задач типа "запустил и забыл" нужно собрать их в коллекцию:
# это набор запускаемых задач background_tasks = set() for i in range(10): task = asyncio.create_task(some_coro(param=i)) # Добавление задачи в набор создает сильную ссылку. background_tasks.add(task) # Чтобы не хранить ссылки на завершенные задачи, необходимо # продумать, чтобы после завершения каждая задача # самостоятельно удаляла свою ссылку из этого набора: task.add_done_callback(background_tasks.discard)
Асинхронный запуск создаваемых задач можно планировать при помощи функции asyncio.gather()
.
Функция asyncio.create_task()
была добавлена в Python 3.7. До Python 3.7, для создания задач, нужно было использовать низкоуровневую функцию asyncio.ensure_future()
:
Изменено в Python 3.8: добавлен аргумент name
.
Изменено в версии 3.11: добавлен аргумент context
.
Обратите внимание, что запускаемые сопрограммы (асинхронные функции) не должны содержать внутри себя операции, блокирующие ход выполнения программы! Другими словами, нельзя какую либо встроенную синхронную функцию (типа socket.getnameinfo()
) обернуть в асинхронную функцию (async def ...
) и думать, что этот код будет работать асинхронно. НЕТ. Такая сопрограмма так же будет блокировать остальной код.
Для запуска из асинхронного кода функций, которые могут блокировать ход выполнения программы используйте субпроцессы
или функцию, доступную с Python 3.9 asyncio.to_thread()
.
В приведенном ниже примере, попробуйте заменить создание задачи create_task(long_operation())
просто на await long_operation()
, чтобы почувствовать разницу. Если будете выполнять этот эксперимент, то не забудьте убрать await task
.
import asyncio async def msg(text): # эмитируем короткую # задержку в выполнении await asyncio.sleep(0.1) print(text) async def long_operation(): # эмитируем долгую # задержку в выполнении print('long_operation started') await asyncio.sleep(3) print('long_operation complete') # основной цикл программы async def main(): # легкая сопрограмма await msg('1 msg complete') # Здесь запустим `long_operation()`, но ждать, пока она # выполнится не хотим, т.к. необходимо получить второе # сообщение как можно раньше. Для этого создаем для нее задачу... task = asyncio.create_task(long_operation()) # легкая сопрограмма await msg('2 msg complete') # Теперь можно дождаться завершения # задачи или отменить ее await task if __name__ == "__main__": # запускаем ВСЕ на выполнение asyncio.run(main()) # 1 msg complete # long_operation started # 2 msg complete # long_operation complete