Задачи и сопрограммы
1. Сопрограммы
Сопрограммы, используемые с asyncio, могут быть реализованы с помощью инструкции async def или с помощью генераторов. Тип сопрограммы async def был добавлен в Python 3.5 и рекомендуется, если нет необходимости поддерживать более старые версии Python.
Генераторные сопрограммы должны быть декорированы @asyncio.coroutine, хотя это не строго обязательно. Декоратор обеспечивает совместимость с async def сопрограммами и также служит документацией. Генераторные сопрограммы используют синтаксис yield from , введённый в PEP 380, вместо исходного синтаксиса yield.
Слово «сопрограмма», подобно слову «генератор», используется для двух разных (хотя и связанных) понятий:
- Функция, определяющая сопрограмму (определение функции с использованием
async defили декорированная с помощью@asyncio.coroutine). Если требуется разграничение, мы будем называть её функцией сопрограммы (iscoroutinefunction()возвращаетTrue). - Объект, полученный вызовом функции сопрограммы. Этот объект представляет вычисление или операцию ввода-вывода (обычно комбинацию), которая в конечном итоге завершится. Если требуется разграничение, мы будем называть его объектом сопрограммы (
iscoroutine()возвращаетTrue).
Что может делать сопрограмма:
-
result = await futureилиresult = yield from future– приостанавливает сопрограмму до завершения будущего, затем возвращает результат будущего или генерирует исключение, которое будет обработано. (Если будущее отменено, оно сгенерирует исключениеCancelledError.) Обратите внимание, что задачи являются будущими, и всё, что сказано о будущих, также относится к задачам. -
result = await coroutineилиresult = yield from coroutine– ждёт, пока другая сопрограмма произведёт результат (или сгенерирует исключение, которое будет обработано). Выражениеcoroutineдолжно быть вызовом другой сопрограммы. -
return expression– производит результат для сопрограммы, которая ожидает этого результата, используяawaitилиyield from. -
raise exception– генерирует исключение в сопрограмме, которая ждёт этого исключения, используяawaitилиyield from.
Вызов сопрограммы не запускает её код – объект сопрограммы, возвращённый вызовом, ничего не делает, пока вы не запланируете её выполнение. Есть два основных способа запуска: вызовите await coroutine или yield from coroutine из другой сопрограммы (предполагая, что другая сопрограмма уже выполняется!), или запланируйте её выполнение с помощью функции ensure_future() или метода AbstractEventLoop.create_task().
Сопрограммы (и задачи) могут выполняться только при запуске цикла событий.
-
@asyncio.coroutine -
Декоратор для обозначения генераторных сопрограмм. Это позволяет генератору использовать
yield fromдля вызоваasync defсопрограмм и также позволяет вызывать генераторasync defсопрограммами, например, с использованием выраженияawait.Нет необходимости декорировать
async defсопрограммы.Если генератор не используется до уничтожения, выводится сообщение об ошибке. См. Обнаружение сопрограмм, которые никогда не планировались.
Примечание
В этой документации некоторые методы документированы как сопрограммы, даже если это обычные функции Python, возвращающие Future. Это сделано намеренно, чтобы предоставить свободу изменения реализации этих функций в будущем. Если такая функция нужна в коде стиля обратного вызова, оберните её результат с помощью ensure_future().
1.1. Пример: Сопрограмма Hello World
Пример сопрограммы, отображающей "Hello World":
import asyncio
async def hello_world():
print("Hello World!")
loop = asyncio.get_event_loop()
# Blocking call which returns when the hello_world() coroutine is done
loop.run_until_complete(hello_world())
loop.close()
См. также
Пример Hello World с call_soon() использует метод AbstractEventLoop.call_soon() для планирования обратного вызова.
1.2. Пример: Сопрограмма отображения текущей даты
Пример сопрограммы, отображающей текущую дату каждую секунду в течение 5 секунд с использованием функции sleep():
import asyncio
import datetime
async def display_date(loop):
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(1)
loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()
См. также
Пример отображения текущей даты с call_later() использует обратный вызов с методом AbstractEventLoop.call_later().
1.3. Пример: Цепочка сопрограмм
Пример цепочки сопрограмм:
import asyncio
async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y
async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
compute() цепляется к print_sum(): print_sum() сопрограмма ждёт, пока compute() завершится, прежде чем вернуть свой результат.
Диаграмма последовательности примера:
«Задача» создаётся методом AbstractEventLoop.run_until_complete(), когда он получает объект сопрограммы вместо задачи.
Диаграмма отображает поток управления, она не описывает точно, как всё работает внутри. Например, сопрограмма сна создаёт внутреннее будущее, которое использует AbstractEventLoop.call_later() для пробуждения задачи через 1 секунду.
2. InvalidStateError
-
exception asyncio.InvalidStateError -
Операция запрещена в данном состоянии.
3. TimeoutError
-
exception asyncio.TimeoutError -
Операция превысила заданный срок.
Примечание
Это исключение отличается от встроенного исключения TimeoutError!
4. Future
-
class asyncio.Future(*, loop=None) -
Этот класс почти совместим с
concurrent.futures.Future.Отличия:
-
result()иexception()не принимают аргумент timeout и генерируют исключение, когда задача ещё не завершена. - Обратные вызовы, зарегистрированные с помощью
add_done_callback(), всегда вызываются черезcall_soon_threadsafe()цикла событий. - Этот класс несовместим с функциями
wait()иas_completed()в пакетеconcurrent.futures.
Этот класс не потокобезопасен.
-
cancel() -
Отменить задачу и запланировать обратные вызовы.
Если задача уже завершена или отменена, вернуть
False. В противном случае изменить состояние задачи на отмененную, запланировать обратные вызовы и вернутьTrue.
-
cancelled() -
Возвращает
True, если задача была отменена.
-
done() -
Возвращает
True, если задача завершена.Завершение означает, что результат / исключение доступны или задача была отменена.
-
result() -
Возвращает результат, представленный этой задачей.
Если задача была отменена, генерирует
CancelledError. Если результат задачи еще недоступен, генерируетInvalidStateError. Если задача завершена и содержит исключение, это исключение генерируется.
-
exception() -
Возвращает исключение, установленное для этой задачи.
Исключение (или
None, если исключение не было установлено) возвращается только если задача завершена. Если задача была отменена, генерируетсяCancelledError. Если задача еще не завершена, генерируетсяInvalidStateError.
-
add_done_callback(fn) -
Добавить обратный вызов, который будет выполнен, когда задача будет завершена.
Обратный вызов вызывается с одним аргументом — объектом задачи. Если задача уже завершена при вызове этого метода, обратный вызов планируется с помощью
call_soon().Используйте functools.partial для передачи параметров в обратный вызов. Например,
fut.add_done_callback(functools.partial(print, "Future:", flush=True))вызоветprint("Future:", fut, flush=True).
-
remove_done_callback(fn) -
Удалить все экземпляры обратного вызова из списка «вызвать при завершении».
Возвращает количество удаленных обратных вызовов.
-
set_result(result) -
Отметить задачу как завершенную и установить её результат.
Если задача уже завершена при вызове этого метода, генерирует
InvalidStateError.
-
set_exception(exception) -
Отметить задачу как завершенную и установить исключение.
Если задача уже завершена при вызове этого метода, генерирует
InvalidStateError.
-
4.1. Пример: Задача с run_until_complete()
Пример, объединяющий Future и функцию корутину:
import asyncio
async def slow_operation(future):
await asyncio.sleep(1)
future.set_result('Future is done!')
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()
Функция корутины отвечает за вычисление (которое занимает 1 секунду) и сохраняет результат в задаче. Метод run_until_complete() ожидает завершения задачи.
Примечание
Метод run_until_complete() использует внутри метод add_done_callback() для получения уведомления о завершении задачи.
4.2. Пример: Задача с run_forever()
Предыдущий пример можно переписать, явно описав поток управления с помощью метода Future.add_done_callback():
import asyncio
async def slow_operation(future):
await asyncio.sleep(1)
future.set_result('Future is done!')
def got_result(future):
print(future.result())
loop.stop()
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
future.add_done_callback(got_result)
try:
loop.run_forever()
finally:
loop.close()
В этом примере задача используется для связи slow_operation() с got_result(): когда slow_operation() завершается, got_result() вызывается с результатом.
5. Задача
-
class asyncio.Task(coro, *, loop=None) -
Планирует выполнение корутины: оборачивает её в будущее. Задача — это подкласс
Future.Задача отвечает за выполнение объекта корутины в цикле событий. Если обернутая корутина получает значение из будущего, задача приостанавливает выполнение обернутой корутины и ждёт завершения будущего. Когда будущее завершено, выполнение обернутой корутины возобновляется с результатом или исключением будущего.
Циклы событий используют кооперативное планирование: цикл событий выполняет только одну задачу за раз. Другие задачи могут выполняться параллельно, если другие циклы событий выполняются в разных потоках. Пока задача ожидает завершения будущего, цикл событий выполняет новую задачу.
Отмена задачи отличается от отмены будущего. Вызов
cancel()генерирует исключениеCancelledErrorв обернутой корутине.cancelled()возвращаетTrueтолько если обернутая корутина не перехватила исключениеCancelledErrorили не подняла исключениеCancelledError.Если ожидающая задача уничтожается, выполнение обернутой корутины не завершено. Вероятно, это ошибка, и записывается предупреждение: см. Уничтожение ожидающей задачи.
Не создавайте напрямую экземпляры
Task: используйте функциюensure_future()или методAbstractEventLoop.create_task().Этот класс не потокобезопасен.
-
classmethod all_tasks(loop=None) -
Возвращает множество всех задач для цикла событий.
По умолчанию возвращаются все задачи для текущего цикла событий.
-
classmethod current_task(loop=None) -
Возвращает выполняющуюся задачу в цикле событий или
None.По умолчанию возвращается текущая задача для текущего цикла событий.
Noneвозвращается при вызове не в контекстеTask.
-
cancel() -
Запросить отмену этой задачи.
Это организует генерацию
CancelledErrorв обернутой корутине в следующем цикле цикла событий. Тогда корутина может очиститься или даже отклонить запрос с помощью try/except/finally.В отличие от
Future.cancel(), это не гарантирует отмену задачи: исключение может быть перехвачено и обработано, отложив отмену задачи или полностью предотвратив её. Задача также может вернуть значение или поднять другое исключение.Сразу после вызова этого метода
cancelled()не вернётTrue(если задача не была уже отменена). Задача помечается как отменённая, когда обернутая корутина завершается с исключениемCancelledError(даже еслиcancel()не вызывался).
-
get_stack(*, limit=None) -
Возвращает список фреймов стека для корутины этой задачи.
Если корутина не завершена, возвращает стек, где она приостановлена. Если корутина успешно завершена или была отменена, возвращает пустой список. Если корутина завершилась с исключением, возвращает список фреймов трассировки.
Фреймы всегда отсортированы от старого к новому.
Необязательный параметр limit задаёт максимальное количество возвращаемых фреймов; по умолчанию возвращаются все доступные фреймы. Его значение различается в зависимости от того, возвращается ли стек или трассировка: возвращаются самые новые фреймы стека, но самые старые фреймы трассировки. (Это соответствует поведению модуля traceback.)
По причинам, не зависящим от нашей воли, для приостановленной корутины возвращается только один фрейм стека.
-
print_stack(*, limit=None, file=None) -
Печатает стек или трассировку для корутины этой задачи.
Это выводит вывод, аналогичный выводу модуля traceback, для фреймов, полученных методом get_stack(). Аргумент limit передаётся в get_stack(). Аргумент file — это поток ввода-вывода, в который записывается вывод; по умолчанию вывод записывается в sys.stderr.
-
5.1. Пример: Параллельное выполнение задач
Пример выполнения 3 задач (A, B, C) параллельно:
import asyncio
async def factorial(name, number):
f = 1
for i in range(2, number+1):
print("Task %s: Compute factorial(%s)..." % (name, i))
await asyncio.sleep(1)
f *= i
print("Task %s: factorial(%s) = %s" % (name, number, f))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
))
loop.close()
Вывод:
Task A: Compute factorial(2)... Task B: Compute factorial(2)... Task C: Compute factorial(2)... Task A: factorial(2) = 2 Task B: Compute factorial(3)... Task C: Compute factorial(3)... Task B: factorial(3) = 6 Task C: Compute factorial(4)... Task C: factorial(4) = 24
Задача автоматически планируется для выполнения при создании. Цикл событий останавливается, когда все задачи завершены.
6. Функции задач
Примечание
В функциях ниже необязательный аргумент loop позволяет явно задать объект цикла событий, используемый для подчинённой задачи или корутины. Если он не указан, используется по умолчанию цикл событий.
-
asyncio.as_completed(fs, *, loop=None, timeout=None) -
Возвращает итератор, значения которого, когда они ожидают, представляют собой экземпляры
Future.Вызывает
asyncio.TimeoutError, если таймаут наступает до завершения всех будущих объектов.Пример:
for f in as_completed(fs): result = yield from f # The 'yield from' may raise # Use resultПримечание
Будущие
fнеобязательно являются членами fs.
-
asyncio.ensure_future(coro_or_future, *, loop=None) -
Планирует выполнение объекта корутины: оборачивает его в будущее. Возвращает объект
Task.Если аргумент —
Future, он возвращается напрямую.Введено в версии 3.4.4.
Изменено в версии 3.5.1: Функция принимает любой awaitable объект.
См. также
-
asyncio.async(coro_or_future, *, loop=None) -
Устаревшее алиас функции
ensure_future().Устарело начиная с версии 3.4.4.
-
asyncio.wrap_future(future, *, loop=None) -
Оборачивает объект
concurrent.futures.Futureв объектFuture.
-
asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False) -
Возвращает будущее, агрегирующее результаты заданных объектов корутин или будущих объектов.
Все будущие объекты должны использовать один и тот же цикл событий. Если все задачи завершены успешно, результатом возвращаемого будущего является список результатов (в порядке исходной последовательности, а не обязательно в порядке прибытия результатов). Если return_exceptions имеет значение True, исключения в задачах обрабатываются так же, как успешные результаты, и собираются в списке результатов; в противном случае первое возникшее исключение будет немедленно передано возвращаемому будущему.
Отмена: если внешнее будущее отменяется, все дочерние объекты (которые ещё не завершены) также отменяются. Если любой дочерний объект отменяется, это рассматривается так, как если бы он поднял
CancelledError— внешнее будущее не отменяется в этом случае. (Это предотвращает отмену одного дочернего объекта, вызывающую отмену других дочерних объектов.)
-
asyncio.iscoroutine(obj) -
Возвращает
Trueесли obj является объектом корутины, который может быть основан на генераторе илиasync defкорутине.
-
asyncio.iscoroutinefunction(func) -
Возвращает
Trueесли func определён как функция корутины, которая может быть декорированной функцией-генератором илиasync defфункцией.
-
asyncio.run_coroutine_threadsafe(coro, loop) -
Передайте объект корутины в заданный цикл событий.
Верните
concurrent.futures.Future, чтобы получить результат.Эта функция предназначена для вызова из другого потока, отличного от потока, в котором выполняется цикл событий. Пример использования:
# Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout) == 3
Если в корутине возникнет исключение, возвращаемый фьючер будет уведомлен. Его также можно использовать для отмены задачи в цикле событий:
try: result = future.result(timeout) except asyncio.TimeoutError: print('The coroutine took too long, cancelling the task...') future.cancel() except Exception as exc: print('The coroutine raised an exception: {!r}'.format(exc)) else: print('The coroutine returned: {!r}'.format(result))См. раздел конкурентности и многопоточности документации.
Примечание
В отличие от других функций модуля,
run_coroutine_threadsafe()требует явного указания аргумента loop.Новое в версии 3.5.1.
-
coroutine asyncio.sleep(delay, result=None, *, loop=None) -
Создайте корутину, которая завершится через заданное время (в секундах). Если result указан, он возвращается вызывающей стороне при завершении корутины.
Точность засыпания зависит от точности цикла событий.
Эта функция является корутиной.
-
asyncio.shield(arg, *, loop=None) -
Дождитесь завершения фьючера, защитив его от отмены.
Выражение:
res = yield from shield(something())
точно эквивалентно выражению:
res = yield from something()
за исключением того, что если корутина, содержащая его, отменяется, задача, выполняемая в
something(), не отменяется. С точки зренияsomething(), отмена не произошла. Но вызывающая сторона все еще отменена, поэтому выражение yield-from по-прежнему вызываетCancelledError. Примечание: Еслиsomething()отменяется другими средствами, это все равно отменитshield().Если вы хотите полностью игнорировать отмену (не рекомендуется), вы можете объединить
shield()с оператором try/except, как показано ниже:try: res = yield from shield(something()) except CancelledError: res = None
-
coroutine asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED) -
Дождитесь завершения заданных в последовательности futures фьючеров и объектов корутин. Корутины будут обернуты в задачи. Возвращает два набора
Future: (завершенные, ожидающие).Последовательность futures не должна быть пустой.
timeout может использоваться для управления максимальным временем ожидания в секундах перед возвратом. timeout может быть целым или дробным числом. Если timeout не указан или
None, ограничений по времени ожидания нет.return_when указывает, когда эта функция должна вернуть результат. Он должен быть одним из следующих констант модуля
concurrent.futures:Константа Описание FIRST_COMPLETEDФункция вернётся, когда любой фьючер завершится или будет отменён. FIRST_EXCEPTIONФункция вернётся, когда любой фьючер завершится с исключением. Если ни один фьючер не вызывает исключение, это эквивалентно ALL_COMPLETED.ALL_COMPLETEDФункция вернётся, когда все фьючеры завершатся или будут отменены. Эта функция является корутиной.
Пример использования:
done, pending = yield from asyncio.wait(fs)
Примечание
Это не вызывает
asyncio.TimeoutError! Фьючеры, которые не завершены при наступлении таймаута, возвращаются во второй набор.
-
coroutine asyncio.wait_for(fut, timeout, *, loop=None) -
Дождитесь завершения единственного
Futureили объекта корутины с таймаутом. Если timeout равенNone, ожидайте завершения фьючера.Корутина будет обернута в
Task.Возвращает результат фьючера или корутины. При наступлении таймаута задача отменяется и генерируется
asyncio.TimeoutError. Чтобы избежать отмены задачи, оберните её вshield().Если ожидание отменено, фьючер fut также отменяется.
Эта функция является корутиной, пример использования:
result = yield from asyncio.wait_for(fut, 60.0)
Изменено в версии 3.4.3: Если ожидание отменено, фьючер fut теперь также отменяется.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/asyncio-task.html