Автор оригинала: Doug Hellmann.
Сопрограммы – это языковые конструкции, предназначенные для параллельной работы. Функция сопрограммы при вызове создает объект сопрограммы, и вызывающий может затем запустить код функции, используя метод send ()
сопрограммы. Сопрограмма может приостановить выполнение с помощью ключевого слова await
с другой сопрограммой. Пока он приостановлен, состояние сопрограммы сохраняется, позволяя ей возобновить работу с того места, где она была остановлена, в следующий раз, когда она будет пробуждена.
Запуск сопрограммы
Есть несколько разных способов запустить сопрограмму из цикла обработки событий asyncio
. Самый простой – использовать run_until_complete ()
, передавая ему сопрограмму напрямую.
asyncio_coroutine.py
import asyncio async def coroutine(): print('in coroutine') event_loop asyncio.get_event_loop() try: print('starting coroutine') coro coroutine() print('entering event loop') event_loop.run_until_complete(coro) finally: print('closing event loop') event_loop.close()
Первый шаг – получить ссылку на цикл событий. Можно использовать тип цикла по умолчанию или создать конкретный класс цикла. В этом примере используется цикл по умолчанию. Метод run_until_complete ()
запускает цикл с объектом сопрограммы и останавливает цикл, когда сопрограмма завершается возвратом.
$ python3 asyncio_coroutine.py starting coroutine entering event loop in coroutine closing event loop
Возвращение значений из сопрограмм
Возвращаемое значение сопрограммы передается обратно в код, который запускается и ожидает его.
asyncio_coroutine_return.py
import asyncio async def coroutine(): print('in coroutine') return 'result' event_loop asyncio.get_event_loop() try: return_value event_loop.run_until_complete( coroutine() ) print('it returned: {!r}'.format(return_value)) finally: event_loop.close()
В этом случае run_until_complete ()
также возвращает результат сопрограммы, которую она ожидает.
$ python3 asyncio_coroutine_return.py in coroutine it returned: 'result'
Цепочка сопрограмм
Одна сопрограмма может запускать другую сопрограмму и ждать результатов. Это упрощает разложение задачи на повторно используемые части. В следующем примере есть две фазы, которые должны выполняться по порядку, но могут выполняться одновременно с другими операциями.
asyncio_coroutine_chain.py
import asyncio async def outer(): print('in outer') print('waiting for result1') result1 await phase1() print('waiting for result2') result2 await phase2(result1) return (result1, result2) async def phase1(): print('in phase1') return 'result1' async def phase2(arg): print('in phase2') return 'result2 derived from {}'.format(arg) event_loop asyncio.get_event_loop() try: return_value event_loop.run_until_complete(outer()) print('return value: {!r}'.format(return_value)) finally: event_loop.close()
Ключевое слово await
используется вместо добавления новых сопрограмм в цикл, поскольку поток управления уже находится внутри сопрограммы, управляемой циклом, поэтому нет необходимости указывать циклу управлять новым сопрограммы.
$ python3 asyncio_coroutine_chain.py in outer waiting for result1 in phase1 waiting for result2 in phase2 return value: ('result1', 'result2 derived from result1')
Генераторы вместо сопрограмм
Функции сопрограмм являются ключевым компонентом дизайна asyncio
. Они предоставляют языковую конструкцию для остановки выполнения части программы, сохранения состояния этого вызова и повторного входа в состояние в более позднее время, которые являются важными возможностями для инфраструктуры параллелизма.
Python 3.5 представил новые языковые функции для определения таких сопрограмм изначально с использованием async def
и для передачи управления с помощью await
, а примеры для asyncio
используют преимущества новая функция. Более ранние версии Python 3 могут использовать функции генератора, заключенные в декоратор asyncio.coroutine ()
и yield from
для достижения того же эффекта.
asyncio_generator.py
import asyncio @asyncio.coroutine def outer(): print('in outer') print('waiting for result1') result1 yield from phase1() print('waiting for result2') result2 yield from phase2(result1) return (result1, result2) @asyncio.coroutine def phase1(): print('in phase1') return 'result1' @asyncio.coroutine def phase2(arg): print('in phase2') return 'result2 derived from {}'.format(arg) event_loop asyncio.get_event_loop() try: return_value event_loop.run_until_complete(outer()) print('return value: {!r}'.format(return_value)) finally: event_loop.close()
В предыдущем примере воспроизводится asyncio_coroutine_chain.py
с использованием функций генератора вместо собственных сопрограмм.
$ python3 asyncio_generator.py in outer waiting for result1 in phase1 waiting for result2 in phase2 return value: ('result1', 'result2 derived from result1')