Programmēšana

Sāciet darbu ar asinhronizāciju Python

Asinhrona programmēšana vai asinhronais Īsāk sakot, tā ir daudzu mūsdienu valodu iezīme, kas ļauj programmai žonglēt ar vairākām operācijām, negaidot un nenokarot nevienu no tām. Tas ir gudrs veids, kā efektīvi apstrādāt tādus uzdevumus kā tīkls vai failu I / O, kur lielākā daļa programmas laika tiek pavadīta, gaidot uzdevuma izpildi.

Apsveriet tīmekļa nokasīšanas lietojumprogrammu, kas atver 100 tīkla savienojumus. Jūs varētu atvērt vienu savienojumu, gaidīt rezultātus, pēc tam atvērt nākamo un gaidīt rezultātus utt. Lielākā daļa laika, kad programma darbojas, tiek pavadīta, gaidot tīkla atbildi, nevis faktiski veicot darbu.

Async nodrošina efektīvāku metodi: atveriet visus 100 savienojumus vienlaikus, pēc tam pārslēdzieties starp katru aktīvo savienojumu, kad tie atgriež rezultātus. Ja viens savienojums nedod rezultātus, pārslēdzieties uz nākamo un tā tālāk, līdz visi savienojumi ir atgriezuši savus datus.

Async sintakse tagad ir standarta Python funkcija, taču ilggadējiem Pythonistas lietotājiem, kuri ir pieraduši darīt vienu lietu vienlaikus, var būt problēmas ap to aplauzt galvu. Šajā rakstā mēs izpētīsim, kā asinhronā programmēšana darbojas Python un kā to izmantot.

Ņemiet vērā, ka, ja vēlaties izmantot asinhronizāciju Python, vislabāk ir izmantot Python 3.7 vai Python 3.8 (šī raksta jaunākā versija). Mēs izmantosim Python asinhronās sintakses un palīga funkcijas, kā noteikts šajās valodas versijās.

Kad izmantot asinhrono programmēšanu

Vislabāk asinhroni ir labāk izmantot, kad mēģināt veikt darbu, kam ir šādas iezīmes:

  • Darba pabeigšana prasa ilgu laiku.
  • Aizkave ietver I / O (diska vai tīkla) darbību gaidīšanu, nevis aprēķināšanu.
  • Darbs ietver daudzas I / O operācijas, kas notiek vienlaikus, vai viena vai vairākas I / O darbības notiek, kad mēģināt paveikt arī citus uzdevumus.

Async ļauj paralēli iestatīt vairākus uzdevumus un tos efektīvi atkārtot, nebloķējot pārējo lietojumprogrammu.

Daži ar asinhronu labi strādājošu uzdevumu piemēri:

  • Tīmekļa nokasīšana, kā aprakstīts iepriekš.
  • Tīkla pakalpojumi (piemēram, tīmekļa serveris vai ietvars).
  • Programmas, kas koordinē rezultātus no vairākiem avotiem, kuru atgriešana prasa daudz laika (piemēram, vienlaicīgi datu bāzes vaicājumi).

Ir svarīgi atzīmēt, ka asinhronā programmēšana atšķiras no daudzsavienošanas vai daudzapstrādes. Visas asinhronizācijas operācijas darbojas vienā pavedienā, taču pēc vajadzības tās viena otrai padodas, padarot asinhronizāciju daudzveidīgāku uzdevumu veikšanai efektīvāku nekā pavedienu izveidošana vai daudzapstrāde. (Vairāk par to zemāk.)

Python asinhronaisgaidi un asinsija

Python nesen pievienoja divus atslēgvārdus, asinhronais un gaidi, lai izveidotu asinhronas darbības. Apsveriet šo skriptu:

def get_server_status (server_addr) # Iespējams, ilgstoša darbība ... return server_status def server_ops () results = [] results.append (get_server_status ('addr1.server') results.append (get_server_status ('addr2.server') return rezultātiem 

Tā paša skripta asinhronā versija - nefunkcionāla, pietiekama, lai dotu priekšstatu par sintakses darbību - varētu izskatīties šādi.

async def get_server_status (server_addr) # Iespējams, ilgstoša darbība ... atgriezt server_status async def server_ops () results = [] results.append (gaidīt get_server_status ('addr1.server') results.append (gaidīt get_server_status ('addr2 serveris ') atgriež rezultātus 

Funkcijas, kas pievienotas prefiksam asinhronais atslēgvārds kļūst par asinhronām funkcijām, kas pazīstamas arī kā korutīnas. Korutīni izturas atšķirīgi no parastajām funkcijām:

  • Korutīnas var izmantot citu atslēgvārdu, gaidi, kas ļauj korutīnai bez bloķēšanas gaidīt rezultātus no citas korutīnas. Kamēr rezultāti atgriezīsies no gaidied coroutine, Python brīvi pārslēdzas starp citām darbojošām koroutine.
  • Korutīnas var tikai saukt no citiem asinhronais funkcijas. Ja skrien server_ops () vai get_server_status () kā tas ir no skripta pamatteksta, jūs nesaņemsit viņu rezultātus; jūs saņemsiet Python coroutine objektu, kuru nevar tieši izmantot.

Tātad, ja mēs nevaram piezvanīt asinhronais funkcijas no nesinhronām funkcijām, un mēs nevaram palaist asinhronais funkcijas tieši, kā mēs tās izmantojam? Atbilde: Izmantojot asinsija bibliotēka, kas tiltus asinhronais un pārējo Python.

Python asinhronaisgaidi un asinsija piemērs

Šeit ir piemērs (atkal, nevis funkcionāls, bet ilustratīvs), kā var rakstīt tīmekļa nokasīšanas lietojumprogrammu, izmantojot asinhronais un asinsija. Šis skripts ņem URL sarakstu un izmanto vairākus URL asinhronais funkcija no ārējas bibliotēkas (lasīt_no_vietas_sinhronizēt ()), lai tos lejupielādētu un apkopotu rezultātus.

importēt asyncio no tīmekļa_scraping_library importēt read_from_site_async async def main (url_list): return wait wait asyncio.gather (* [read_from_site_async (_) for _ in url_list]) urls = ['//site1.com'''//othersite.com', '//newsite.com'] rezultāti = asyncio.run (main (urls)) print (rezultāti) 

Iepriekš minētajā piemērā mēs izmantojam divus kopīgus asinsija funkcijas:

  • asyncio.run () tiek izmantots, lai palaistu asinhronais funkciju no asinhronās mūsu koda daļas, un tādējādi sākt visas programmas asinhronās darbības. (Tā mēs skrienam galvenais ().)
  • asyncio.gather () izmanto vienu vai vairākas asinhroni dekorētas funkcijas (šajā gadījumā vairākas lasīt_ no vietnes_sinhronizēt () no mūsu hipotētiskās tīmekļa nokasīšanas bibliotēkas), vada tos visus un gaida visu rezultātu ienākšanu.

Ideja ir tāda, ka mēs sākam lasīšanas darbību visām vietnēm vienlaikus pulcēties rezultāti, kad tie nonāk (tātad asyncio.gather ()). Pirms mēs pārejam uz nākamo, mēs negaidām, kamēr tiks pabeigta viena darbība.

Python asinhrono lietotņu komponenti

Mēs jau esam minējuši, kā Python asinhronās lietotnes kā galveno sastāvdaļu izmanto korutīnus, izmantojot asinsija bibliotēku, lai tos vadītu. Daži citi elementi ir arī galvenie asinhronajām lietojumprogrammām Python:

Notikumu cilpas

The asinsija bibliotēka veido un pārvalda notikumu cilpas, mehānismi, kas vada korutīnas līdz to pabeigšanai. Python procesā vienlaikus vajadzētu darboties tikai vienai notikumu cilpai, kaut vai tikai tāpēc, lai programmētājam būtu vieglāk sekot līdzi tam, kas tajā notiek.

Uzdevumi

Iesniedzot korutīnu notikumu lokā apstrādei, jūs varat atgriezties Uzdevums objekts, kas nodrošina veidu, kā kontrolēt korutīna uzvedību ārpus notikuma cilpas. Piemēram, ja jums ir jāatceļ darbojošais uzdevums, to varat izdarīt, piezvanot uz uzdevumu . atcelt () metodi.

Šeit ir nedaudz atšķirīga vietnes skrāpētāja skripta versija, kas parāda notikumu ciklu un uzdevumus darbā:

importēt asyncio no tīmekļa_scraping_library importēt read_from_site_async uzdevumus = [] async def main (url_list): n vietnei url_list: task.append (asyncio.create_task (read_from_site_async (n))) drukas uzdevumu atgriešana gaida asyncio.gather (* = ['//site1.com','//othersite.com','//newsite.com'] cilpa = asyncio.get_event_loop () results = loop.run_until_complete (main (urls)) druka (rezultāti) 

Šis skripts precīzāk izmanto notikuma cilpu un uzdevumu objektus.

  • The .get_event_loop () metode nodrošina mūs ar objektu, kas ļauj mums tieši kontrolēt notikumu loku, programmatiski iesniedzot asinhronās funkcijas, izmantojot .run_until_complete (). Iepriekšējā skriptā mēs varējām palaist tikai vienu augstākā līmeņa asinhronizācijas funkciju, izmantojot asyncio.run (). Starp citu, .run_until_complete () dara tieši to, ko saka: Tas izpilda visus piegādātos uzdevumus, līdz tie ir pabeigti, pēc tam atgriež rezultātus vienā partijā.
  • The .create_task () metode paņem funkciju, ieskaitot tās parametrus, un dod mums atpakaļ Uzdevums objektu, lai to palaistu. Šeit mēs katru URL iesniedzam kā atsevišķu Uzdevums uz notikuma cilpu un saglabājiet Uzdevums objekti sarakstā. Ņemiet vērā, ka mēs to varam izdarīt tikai notikuma cilpas iekšienē, tas ir, iekš asinhronais funkciju.

Cik liela kontrole jums nepieciešama notikuma ciklam un tā uzdevumiem, būs atkarīgs no tā, cik sarežģīta ir jūsu veidotā lietojumprogramma. Ja vēlaties vienkārši iesniegt noteiktu darbu kopu, kas darbotos vienlaicīgi, tāpat kā ar mūsu tīmekļa skrāpi, jums nebūs nepieciešama liela kontrole - tieši tik daudz, lai sāktu darbus un apkopotu rezultātus.

Turpretī, ja veidojat pilnvērtīgu tīmekļa sistēmu, jūs vēlaties daudz vairāk kontrolēt korutīnu un notikumu cilpas darbību. Piemēram, lietojumprogrammas avārijas gadījumā jums, iespējams, būs jāatslēdz notikuma cilpa, vai arī, ja notikuma cilpu izsaucat no citas pavediena, palaidiet uzdevumus drošā veidā.

Asinch vs threading vs multiprocessing

Šajā brīdī jums var rasties jautājums, kāpēc pavedienu vai daudzapstrādes vietā jāizmanto asinhronizācija, kas abi jau sen ir pieejami Python?

Pirmkārt, pastāv galvenā atšķirība starp asinhrono versiju un pavedieniem vai daudzapstrādi, pat neatkarīgi no tā, kā šīs lietas tiek ieviestas Python. Async ir apmēram vienlaikus, savukārt pavedieni un daudzapstrāde ir aptuveni paralēlisms. Vienlaicīgums nozīmē efektīvu laika sadalīšanu starp vairākiem uzdevumiem vienlaikus, piemēram, e-pasta adreses pārbaudi, gaidot reģistru pārtikas preču veikalā. Paralēlisms ietver vairākus aģentus, kas līdzās apstrādā vairākus uzdevumus, piemēram, pārtikas veikalā ir atvērti pieci atsevišķi reģistri.

Lielāko daļu laika async ir labs vītņu aizstājējs, jo vītņošana tiek ieviesta Python. Tas ir tāpēc, ka Python izmanto nevis OS pavedienus, bet gan savus sadarbības pavedienus, kur tulkā vienlaikus vienlaikus darbojas tikai viens pavediens. Salīdzinot ar sadarbības pavedieniem, async nodrošina dažas galvenās priekšrocības:

  • Async funkcijas ir daudz vieglākas nekā pavedieni. Desmitiem tūkstošu asinhrono darbību, kas darbojas vienlaikus, būs daudz mazāk nekā desmitiem tūkstošu pavedienu.
  • Asinhkoda struktūra ļauj vieglāk spriest par to, kur uzdevumi tiek uzņemti un atstāti. Tas nozīmē, ka datu sacīkstes un diegu drošība nav tik liela problēma. Tā kā visi asinhrono notikumu cilpas uzdevumi darbojas vienā pavedienā, Python (un izstrādātājam) ir vieglāk sērijveidot to, kā viņi piekļūst atmiņas objektiem.
  • Asinhronizācijas darbības var atcelt un manipulēt vieglāk nekā pavedienus. The Uzdevums objekts, no kura mēs atgriežamies asyncio.create_task () nodrošina mums ērtu veidu, kā to izdarīt.

No otras puses, daudzapstrāde Python ir vislabākā darbiem, kas ir saistīti ar CPU, nevis ar I / O. Async faktiski darbojas roku rokā ar daudzapstrādi, kā jūs varat izmantot asyncio.run_in_executor () deleģēt procesora ietilpīgus darbus procesu centram no centrālā procesa, neaizkavējot šo centrālo procesu.

Nākamās darbības ar Python asinhronizāciju

Vislabākais, kas jādara, ir izveidot dažas vienkāršas savas asinhronās lietotnes. Labu piemēru tagad ir daudz, kad asinhronā programmēšana Python ir izgājusi dažas versijas un pāris gadu laikā bija jāapmetas un jākļūst plašākai. Oficiālā dokumentācija asinsija ir vērts izlasīt, lai redzētu, ko tas piedāvā, pat ja jūs neplānojat izmantot visas tā funkcijas.

Jūs varētu arī izpētīt arvien pieaugošo ar asinhronizēto bibliotēku un starpprogrammatūru skaitu, no kurām daudzas nodrošina asinhronas, nebloķējošas datubāzes savienotāju, tīkla protokolu un tamlīdzīgu versijas. The aio-libs repozitorijā ir daži galvenie, piemēram, aiohittp bibliotēka piekļuvei tīmeklim. Ir arī vērts meklēt Python pakešu rādītājā bibliotēkas ar asinhronais atslēgvārds. Izmantojot kaut ko līdzīgu asinhronai programmēšanai, labākais veids, kā mācīties, ir redzēt, kā citi to ir izmantojuši.

$config[zx-auto] not found$config[zx-overlay] not found