Как да използвам asyncio в Python

Асинхронната функционалност на Python за асинхронно програмиране или накратко async ви позволява да пишете програми, които да свършат повече работа, без да чакате независимите задачи да завършат. В asyncioбиблиотеката са включени с Python ви дава инструментите за ползване асинхронен за обработка на диск или мрежа I / O, без да прави всичко останало изчакайте.

asyncio предоставя два вида API за справяне с асинхронни операции:  високо ниво  и  ниско ниво . Приложните програмни интерфейси (API) на високо ниво са най-общо полезни и са приложими за най-голямото разнообразие от приложения. Приложните програмни интерфейси (API) на ниско ниво са мощни, но и сложни и се използват по-рядко.

В тази статия ще се концентрираме върху API на високо ниво. В секциите по-долу ще разгледаме най-често използваните приложни програмни интерфейси (API) на високо ниво  asyncioи ще покажем как те могат да се използват за често срещани операции, включващи асинхронни задачи. 

Ако сте напълно нови в асинхронизирането в Python или бихте могли да използвате опреснител за това как работи, прочетете моето въведение в Python async, преди да се потопите тук.

Стартирайте съпрограми и задачи в Python

Естествено, най-честата употреба за asyncioе да се изпълняват асинхронните части на вашия Python скрипт. Това означава да се научите да работите с подпрограми и задачи. 

Асинхронните компоненти на Python, включително съпрограми и задачи, могат да се използват само с други асинхронни компоненти, но не и с конвенционален синхронен Python, така че трябва  asyncio да преодолеете празнината. За да направите това, използвате  asyncio.run функцията:

внос asyncio

async def main ():

print ("Изчаква се 5 секунди.")

за _ в обхват (5):

очаквам asyncio.sleep (1)

print (".")

print ("Готово изчакване.")

asyncio.run (main ())

Това се изпълнява  main(), заедно с всички съпрограми  main() и се чака, за да се върне резултат.

Като общо правило програмата на Python трябва да има само един  .run() израз, както програмата на Python трябва да има само една  main() функция. Async, ако се използва небрежно, може да направи контролния поток на програма труден за четене. Наличието на една входна точка към асинхронния код на програмата предпазва нещата от косми.

Асинхронните функции също могат да бъдат насрочени като  задачи или обекти, които обгръщат съпрограмите и помагат за тяхното изпълнение.

async def my_task ():

направи нещо()

задача = asyncio.create_task (my_task ())

my_task() след това се изпълнява в цикъла на събитията, като резултатите се съхраняват в  task.

Ако имате само една задача, от която искате да получите резултати, можете да използвате, за  asyncio.wait_for(task) да изчакате задачата да завърши, след което да използвате  task.result() за извличане на резултата. Но ако сте планирали изпълнение на няколко задачи и искате да изчакате  всички  да приключат, използвайте  asyncio.wait([task1, task2]) за събиране на резултатите. (Имайте предвид, че можете да зададете време за изчакване на операциите, ако не искате те да се изпълняват след определен период от време.)

Управление на асинхронния цикъл на събития в Python

Друга често срещана употреба за  asyncio е управлението на асинхронния  цикъл на събития . Цикълът на събитията е обект, който изпълнява асинхронни функции и обратно извикване; създава се автоматично, когато използвате  asyncio.run(). Обикновено искате да използвате само един цикъл на асинхронно събитие за програма, за да запазите нещата управляеми.

Ако пишете по-усъвършенстван софтуер, като сървър, ще ви е необходим достъп на по-ниско ниво до цикъла на събитията. За тази цел можете да „повдигнете капака“ и да работите директно с вътрешните елементи на цикъла на събитията. Но за прости работни места няма да е необходимо.

Четете и записвайте данни с потоци в Python

Най-добрият сценарий за асинхронизация са дълготрайни мрежови операции, при които приложението може да блокира чакането на някакъв друг ресурс да върне резултат. За тази цел  asyncio предлага потоци, които са механизми на високо ниво за извършване на мрежови I / O. Това включва дейността като сървър за мрежови заявки.

asyncio използва два класа  StreamReader и  StreamWriter, за да чете и пише от мрежата на високо ниво. Ако искате да четете от мрежата, бихте използвали  asyncio.open_connection() за отваряне на връзката. Тази функция връща кортеж от  StreamReader и  StreamWriter обекти, както и можете да използвате  .read() и  .write() методи на всеки да общуват.

За да получавате връзки от отдалечени хостове, използвайте  asyncio.start_server(). Най- asyncio.start_server()функцията се като аргумент и функция за обратна,  client_connected_cb, който се нарича, когато получи заявка. Тази функция за обратно извикване приема екземпляри  StreamReader и StreamWriter като аргументи, така че можете да обработвате логиката за четене / запис на сървъра. (Вижте тук за пример за прост HTTP сървър, който използва   библиотеката asyncio-driven  aiohttp.)

Синхронизирайте задачите в Python

Асинхронните задачи обикновено се изпълняват изолирано, но понякога ще искате те да комуникират помежду си. asyncio осигурява опашки и няколко други механизма за синхронизиране между задачите:

  • Опашкиasyncio опашките позволяват на асинхронни функции да подреждат Python обекти, които да се консумират от други асинхронни функции - например за разпределяне на натоварванията между различни видове функции въз основа на тяхното поведение.
  • Примитиви на синхронизацията : Заключва, събития, условия и семафори в asyncioработата като техните конвенционални колеги на Python. 

Едно нещо, което трябва да имате предвид за всички тези методи, е, че те  не  са безопасни за нишките. Това не е проблем за асинхронни задачи, изпълняващи се в същия цикъл на събития. Но ако се опитвате да споделяте информация със задачи в различен цикъл на събития, нишка на операционна система или процес, ще трябва да използвате  threading модула и неговите обекти, за да го направите.

Освен това, ако искате да  стартирате  съпрограми през границите на нишките, използвайте  asyncio.run_coroutine_threadsafe() функцията и предайте цикъла на събитията, за да използвате с нея като параметър.

Поставете на пауза съпрограма в Python

Друга често използвана  asyncioи недостатъчно обсъждана е изчакване на произволен период от време в съпрограмата. Не можете да използвате  time.sleep() за това, или ще блокирате цялата програма. Вместо това използвайте  asyncio.sleep(), което позволява на други съпрограми да продължат да се изпълняват.

Използвайте асинхронизиране на по-ниско ниво в Python

И накрая, ако смятате, че приложението, което изграждате, може да изисква asyncioкомпоненти от по-ниско ниво, огледайте се, преди да започнете да кодирате: Има голяма вероятност някой вече да е изградил асинхронна Python библиотека, която прави това, от което се нуждаете.

Например, ако имате нужда от асинхронни DNS заявки, проверете  aiodns библиотеката и за асинхронни SSH сесии има  asyncSSH. Търсете в PyPI по ключовата дума „async“ (плюс други ключови думи, свързани със задачата) или проверете ръчно подготвения Awesome Asyncio списък за идеи.