Asinhrono programiranje v Pythonu: od osnov do uporabe v resničnem svetu

Zadnja posodobitev: 03/17/2026
  • Asinhrono programiranje v Pythonu omogoča, da se več nalog, vezanih na V/I, izvaja brez medsebojnega blokiranja prek async, await in zanko dogodkov.
  • Z uporabo orodij, kot je asyncio, aiohttp, asinhroni upravitelji konteksta in asinhrona iteracija omogočajo skalabilno omrežje in delovne obremenitve, ki zahtevajo veliko API-ja.
  • Async se odlično obnese pri omrežnem in datotečnem vhodno/izhodnem poslovanju, vendar ga je treba dopolniti z večprocesorskimi ali specializiranimi storitvami za naloge, vezane na procesor.
  • Dobre prakse – izogibanje blokiranju klicev, omejevanje sočasnosti in obravnavanje napak na nalogo – so ključne za pisanje zanesljivih asinhronih aplikacij.

asinhrono programiranje v Pythonu

Asinhrono programiranje v Pythonu je iz nišne teme prešlo v eno ključnih veščin za vsakogar, ki gradi sodobne, odzivne aplikacije. Če delate s spletnimi API-ji, mikroservisi, nadzornimi ploščami v realnem času ali katero koli vrsto obsežnega vhodno/izhodnega (V/I) delovanja, ste verjetno naleteli na težavo, kjer vaša koda porabi več časa za čakanje kot za dejansko delo. Prav tam se izkažejo asinhrone tehnike.

Namesto da bi pustili, da vaš program miruje med čakanjem na omrežje, disk ali zunanjo storitev, vam asinhrona koda omogoča prekrivanje teh čakalnih obdobij in ohranjanje delovanja aplikacije. V tem priročniku se bomo poglobili v delovanje funkcije async v Pythonu, katere težave rešuje, kdaj resnično pomaga in kdaj je napačno orodje, ter si ogledali konkretne primere uporabe. async, await, asyncio in priljubljene asinhrone knjižnice, kot so aiohttp.

Kaj je asinhrono programiranje v Pythonu?

V svojem bistvu je asinhrono programiranje način strukturiranja kode, tako da lahko več nalog napreduje, ne da bi se med seboj blokirale, tudi če si delijo eno samo nit operacijskega sistema. V klasičnem sinhronem slogu se vsaka operacija konča, še preden se naslednja sploh začne: pokliče se API, počaka, razčleni se odgovor in šele nato se nadaljuje. Z asinhrono kodo lahko sprožite več dolgotrajnih operacij in pustite, da Python preklaplja med njimi, kadar koli ena od njih samo čaka.

Python ta model implementira s kombinacijo posebne sintakse in kooperativnega razporejevalnika, zgrajenega okoli zanke dogodkov. Dve ključni besedi, ki odkleneta vse to, sta async in await: funkcije označite kot asinhrone z uporabo async defin se v njih ustavite z await vsakič, ko zadenete operacijo, ki lahko nadzor vrne v zanko dogodkov.

An async def Funkcija ne vrne vrednosti neposredno; vrne objekt korutine, ki predstavlja izračun, ki ga je mogoče načrtovati in čakati. Ko uporabljate await Znotraj te funkcije Python začasno ustavi trenutno korutino in pusti, da se druge čakajoče naloge izvajajo, dokler se pričakovana operacija (kot je omrežna zahteva) ne zaključi, nakar se izvajanje nadaljuje takoj po await.

To je ključnega pomena: asinhrona koda Python je običajno še vedno enonitna, vendar sočasna v smislu, da več operacij napreduje v prekrivajočih se časovnih oknih. Medtem ko ena naloga čaka na V/I, druga naloga dobi čas procesorja. Zato je asinhronost odlična za delovne obremenitve, vezane na V/I, vendar ne pospeši čarobno dela, ki zahteva veliko procesorja.

zanka dogodkov python asyncio

Konkretna analogija: Šahovske razstave in čas čakanja

Klasična analogija, ki se v skupnosti Python uporablja za razlago sočasnega izvajanja v primerjavi z zaporednim, izvira iz simultane šahovske razstave. Predstavljajte si velemojstrico, ki igra proti 24 amaterjem. Dogodek lahko vodi na dva različna načina, s sinhrono in asinhrono strategijo.

V sinhroni različici se usede z enim nasprotnikom in odigra to eno igro od začetka do konca, preden se premakne k naslednji mizi. Vsaka njena poteza traja 5 sekund, medtem ko vsak amater porabi približno 55 sekund za razmišljanje. Tipična igra ima 30 izmenjav potez (torej skupno 60 potez). To pomeni, da vsaka igra traja (55 + 5) × 30 = 1800 sekund, približno 30 minut. S 24 igrami se celoten dogodek vleče 12 ur.

V asinhroni različici hodi po sobi in naredi eno potezo na vsaki plošči, nato pa takoj preide na naslednjo, medtem ko trenutni nasprotnik razmišlja o svojem odgovoru. En krog potez na 24 ploščah traja 24 × 5 = 120 sekund oziroma 2 minuti. Po 30 takih krogih je celoten sklop iger končan v približno 3600 sekundah, tj. 1 uri.

Pomembno je, da se njena surova hitrost igre ni nikoli spremenila; spremenilo se je le to, kako je izkoristila čas čakanja nasprotnikov. Asinhrona koda v Pythonu sledi istemu načelu: ne pospeši V/I, vendar poskrbi, da počnete nekaj koristnega, medtem ko bi sicer čakali na omrežje, disk ali kateri koli zunanji vir.

Zahteve za sinhronizacijo v primerjavi z asinhronimi zahtevami: primer iz resničnega sveta z API-ji

Eden najpogostejših primerov uporabe asinhronosti v Pythonu je delo z zunanjimi API-ji, kjer lahko vsaka zahteva zlahka traja več sto milisekund ali več. Za ponazoritev si predstavljajte, da želite pridobiti število sledilcev za več računov GitHub z uporabo njihovega javnega API-ja.

Preprost sinhroni pristop bi uporabil priljubljenega blokirajočega odjemalca HTTP, kot je requests. Za vsako končno točko uporabnika bi v zanki izvedli zahtevo GET, prebrali koristni tovor JSON in izvlekli followers polje in ga natisnite ali shranite. To je preprosto in berljivo, vendar ima slabost: za vsak račun, ki ga obdelate, program izvede zahtevo in nato le počaka na odgovor, preden sploh začne naslednjega.

Torej, če označite tri uporabnike, kot so api.github.com/users/python, api.github.com/users/google in api.github.com/users/firebase, koda pošlje prvo zahtevo, se blokira, dokler se GitHub ne odzove, nato se premakne na drugo zahtevo in tako naprej. Z nekaj uporabniki je to morda sprejemljivo, toda ko vaš seznam raste na stotine ali tisoče, se skupni čas obdelave močno poveča, ker vaša aplikacija večino svoje življenjske dobe preživi v mirovanju in čaka na oddaljeni strežnik.

Za pospešitev lahko preklopite na asinhrono implementacijo, zgrajeno na asyncio in asinhronsko zmogljiv HTTP odjemalec, kot je aiohttp. V tem modelu zaženete več nalog s korutinami, ki skoraj hkrati sprožijo svoje zahteve HTTP. Zanka dogodkov nato čaka na odgovore katere koli od njih in nadaljuje vsako nalogo, ko prispejo podatki, namesto da čaka, da se ena zahteva v celoti zaključi, preden začne naslednjo.

Ko primerjate ta dva pristopa drug ob drugem, asinhrona različica običajno zmaga z veliko prednostjo, še posebej, ko se število uporabnikov poveča. Čas na zahtevo se ne spremeni, vendar se skupni čas za pridobitev vseh rezultatov močno zmanjša, ker obravnavate veliko povezav hkrati namesto zaporedno.

Osnovni koncepti: korutine, zanka dogodkov, naloge in prihodnosti

Pod pokrovom se sodobni asinhroni Python vrti okoli nekaj ključnih gradnikov, ki jih zagotavlja predvsem asyncio modul. Razumevanje teh konceptov bo preostali del ekosistema naredilo veliko manj skrivnosten in vam pomagalo pri oblikovanju robustnih asinhronih arhitektur.

Korutina je posebna vrsta funkcije, ki lahko začasno ustavi in ​​nadaljuje lastno izvajanje. V današnji sintaksi definiramo eno z async defKo ga pokličete, dobite objekt korutine, ki ga je treba počakati ali načrtovati; ne izvede se takoj do konca kot običajna funkcija. V notranjosti, kadar koli uporabite await Pri čakajoči obstoječi ...

Dogodkovna zanka je orkestrator, ki spremlja vse čakajoče korutine, vhodno/izhodne operacije in časovnike ter odloča, kateri del kode se bo zagnal v danem trenutku. V preteklosti ste morali zanko pridobiti in upravljati eksplicitno prek asyncio.get_event_loop(), vendar je v sodobni kodi Python prednostni vzorec pustiti asyncio.run() ustvarite, zaženite in zaprite zanko okoli asinhrone funkcije najvišje ravni, kot je main().

Naloge so ovojnice okoli korutin, ki zanki dogodkov naročijo, naj jih načrtuje za izvedbo. Lahko si jih predstavljate kot lahka opravila: zanka lahko prepleta napredek med številnimi opravili, ne da bi sprožila več niti. Običajno ustvarite opravila z asyncio.create_task() ali s klicem pomočnikov, kot so asyncio.gather(), ki interno upravljajo zbirko nalog.

Futures predstavljajo rezultate, ki bodo na voljo kasneje, podobno kot Promises v JavaScriptu. Tako naloge kot prihodnosti so objekti, ki jih je mogoče pričakovati: lahko await jih začasno ustavijo, dokler se osnovna operacija ne konča. Ta poenoten protokol močno poenostavi orkestrsko kodo, saj se sestavljanje asinhronih tokov skrči na čakanje na prave objekte v pravem vrstnem redu.

Asinhrona sintaksa v praksi: async, await, async with in async for

Naš async Ključna beseda ni omejena na definicije funkcij; razširja se tudi na upravitelje konteksta in zanke, tako da lahko v svetu asinhronih funkcij sodelujejo naprednejši vzorci. Poznavanje te razširjene sintakse vam pomaga pisati elegantno kodo za omrežne povezave, seje, tokove in protokole po meri.

Najpogostejša oblika je async def, ki definira asinhrono funkcijo (tovarno korutin). Znotraj takšne funkcije boste obilno uporabljali await vsakič, ko pokličete drugo korutino ali operacijo, ki jo je mogoče čakati, kot na primer asyncio.sleep(), asinhrona zahteva HTTP ali asinhrona poizvedba v zbirki podatkov. Upoštevajte, da ne morete uporabiti await neposredno na najvišji ravni skripte; mora biti znotraj async def.

Čeprav bi vas morda mikalo, da pokličete time.sleep() znotraj vaše korutine za zamude, bi to popolnoma izničilo namen uporabe asinhronosti. time.sleep() blokira celotno nit, vključno z zanko dogodkov, zato v tem času ne more potekati nobena druga asinhronska naloga. Namesto tega morate uporabiti neblokirajočo različico. asyncio.sleep(), ki med odštevanjem časa vrne nadzor v zanko.

Python podpira tudi asinhrone upravljalnike konteksta prek async with, implementirano z definiranjem posebnih metod __aenter__ in __aexit__. To je še posebej priročno pri delu z objekti, ki potrebujejo čisto zaporedje namestitve in razgradnje, ki vključuje asinhrone operacije, kot je odpiranje omrežne seje ali pridobivanje asinhronega vira. Tipičen primer je upravljanje aiohttp.ClientSession ali posamezno zahtevo HTTP z uporabo async with blokov namesto ročnega klicanja close().

Končno je asinhrona iteracija izpostavljena prek async for, ki se zanaša na magične metode __aiter__ in __anext__ opisano v PEP 492. Asinhroni iteratorji in asinhroni generatorji vam omogočajo, da sčasoma ustvarite elemente z uporabo await znotraj iteracijskega procesa, kar je idealno za pretakanje podatkov, ki postopoma prihajajo prek omrežja ali iz drugega asinhronega vira.

Sočasno izvajanje več nalog z asyncio

Prava moč asinhronega programiranja se pokaže, ko hkrati izvajate več nalog, vezanih na V/I, namesto eno za drugo. V Python-ovem asinhronem ekosistemu so glavna orodja za to asyncio.create_task() in asyncio.gather(), ki oba načrtujeta korutine v zanki dogodkov.

z asyncio.gather(), lahko zaženete več korutin naenkrat in počakate, da se vse končajo, njihove rezultate pa prejmete kot seznam ali nabor. To je izjemno pogosto pri serijah klicev HTTP, poizvedbah v zbirki podatkov ali kateri koli ponavljajoči se asinhroni operaciji. V osnovi, gather() vsako korutino zavije v nalogo in zagotovi, da so vse izvedene do konca.

Če se vrnemo k primeru pridobivanja profilov GitHub, vendar ga preoblikujemo z uporabo aiohttp in asyncio.gather(), boste na koncu imeli tri klice funkcije, kot je fetch_user() ki se lansirajo hkrati. Vsaka naloga začne svojo zahtevo HTTP, med čakanjem na podatke preda nadzor in nato nadaljuje z razčlenjevanjem odgovora, ko ta prispe. Z vidika uporabnika se vsi trije rezultati prikažejo približno hkrati.

Vendar pa obstajajo primeri, ko ne želite zagnati tisočev ali milijonov nalog hkrati, ker bi to lahko preobremenilo vaš računalnik ali doseglo zunanje omejitve hitrosti. Pogost vzorec je omejitev sočasnosti samo z obdelavo MAX_TASKS operacije hkrati, bodisi z uporabo semaforjev, omejenih bazenov ali logike ročnega paketnega združevanja znotraj vašega asinhronega delovnega toka.

Drug ključni vidik pri sočasnem izvajanju več nalog je, kako ravnate z napakami; v resničnih aplikacijah je redko sprejemljivo, da ena sama neuspešna zahteva povzroči sesutje celotnega paketa. V idealnem primeru bi morala vaša asinhrona orkestracija loviti in upravljati izjeme za vsako nalogo posebej, jih morda beležiti, selektivno poskušati znova ali vrniti delne rezultate, preostanek paketa pa ohraniti nedotaknjen.

Obvladovanje sočasnosti: prednosti in pasti

Pomembno je, da v mislih ločite pojma sočasnosti in vzporednosti, ker asinhroni Python omogoča prvo, ne pa nujno tudi drugo. Sočasnost pomeni, da več nalog napreduje v prekrivajočih se intervalih, medtem ko vzporednost pomeni, da se dobesedno izvajajo hkrati na več jedrih procesorja.

Tipična asinhrona koda z uporabo asyncio ne ustvarja več niti operacijskega sistema; namesto tega multipleksira naloge v eni sami niti glede na to, kdaj je vsaka blokirana pri V/I, podobno kot programación asíncrona en Node.js. Zato se tako dobro prilagaja s tisoči povezav: preklapljanje konteksta je poceni, ker je kooperativno in ga nadzoruje zanka dogodkov in ne operacijski sistem.

Ta zasnova prinaša izzive, zlasti glede koordinacije in obravnave izjem. Ker je vaša logika zdaj razpršena po več korutinah, ki se časovno prepletajo, morate biti bolj premišljeni pri deljenju stanja, širjenju napak in čiščenju virov. Napake, kot so pozabljene await, naloge, ki niso nikoli pričakovane, ali izjeme, ki jih naloge v ozadju tiho pogoltnejo, so lahko subtilne in jih je težko odpraviti.

Da bi vaša asinhrona koda ostala vzdržna, morate upoštevati trdne inženirske prakse: osredotočite korutine na eno samo odgovornost, centralizirajte obravnavo napak, kjer je to mogoče, in dodajte ustrezno beleženje, da boste razumeli, kaj se dogaja med izvajanjem. Dobra orodja in jasne konvencije močno pripomorejo k preprečevanju težav, podobnih tekmovalnim pogojem, ali uhajanja virov, tudi v enonitnem asinhronem okolju.

Kdaj asinhronska koda resnično pomaga (in kdaj ne)

Asinhrono programiranje je neverjetno učinkovito za delovne obremenitve, vezane na V/I, vendar ni čarobna rešitev za vse težave z zmogljivostjo. Prvi korak pri vsaki optimizaciji bi moral biti ugotoviti, ali ozka grla izvirajo iz V/I ali iz računanja, vezanega na CPU.

Če vaša aplikacija večino časa čaka na omrežne odgovore, bere in piše datoteke, poizveduje po bazah podatkov ali komunicira prek vtičnic, potem je asinhronost skoraj zagotovo dobra izbira. Tipični primeri vključujejo spletne API-je, ki komunicirajo z več zunanjimi storitvami, ETL cevovode, ki hkrati berejo iz več virov podatkov in pišejo vanje, in mikrostoritve, ki vzdržujejo veliko hkratnih povezav odjemalcev.

Po drugi strani pa, če vašo delovno obremenitev predstavljajo obsežne operacije procesorja, kot so računanje, obdelava slik ali kompleksne simulacije, sama asinhronost ne bo pospešila stvari. V takih primerih GIL (Global Interpreter Lock) še vedno omejuje, kaj se lahko izvaja vzporedno znotraj enega samega Python procesa. Običajno boste boljše rezultate dosegli z večprocesorsko obdelavo, izvornimi razširitvami ali uporabo specializiranih zalednih sistemov.

V poslovnih okoljih je pragmatična strategija kombinacija teh tehnik: uporaba asinhronih in asinhronih SDK-jev za storitve v oblaku (AWS, Azure in druge) za zmanjšanje zakasnitve in povečanje prepustnosti, hkrati pa delegiranje dela, ki intenzivno uporablja CPE, na ločene procese, delavce ali upravljane računalniške storitve. Na ta način izkoristite prednosti vsakega orodja, namesto da se borite proti izvajalnemu okolju jezika.

Najboljše prakse za pisanje asinhronih kod v Pythonu

Ko boste začeli širše uporabljati asinhronost, vam bodo določeni vzorci in navade pomagali preprečiti najpogostejše pasti. Prav tako olajšajo razumevanje kode za soigralce, ki morda še niso dobro seznanjeni z asinhronim ekosistemom.

Temeljno pravilo je, da se izogibate blokiranju klicev v asinhronih kodnih poteh. To pomeni zamenjavo stvari, kot so time.sleep() z await asyncio.sleep()in previdnost pri knjižnicah, ki ne ponujajo asinhronih API-jev. Če je paket tretje osebe zgolj sinhroni, lahko njegovo obsežno klicanje iz korutine blokira zanko dogodkov in uniči vaše prednosti sočasnosti.

Kadar koli imate več neodvisnih vhodno/izhodnih operacij, jih raje izvajajte sočasno z uporabo pripomočkov, kot je asyncio.gather() ali skupine nalog, omejene z najvišjo stopnjo sočasnosti. Ta vzorec poveča prepustnost, hkrati pa ohranja nadzor nad številom odprtih povezav ali zahtev med delovanjem.

Kot vodilo za oblikovanje poskušajte ohraniti korutine relativno majhne in osredotočene na jasno odgovornost, podobno kot bi oblikovali funkcije v čisti sinhroni kodi. Velike monolitne korutine, ki združujejo mreženje, poslovno logiko in obravnavo napak, hitro postanejo težke za testiranje in sklepanje, zlasti ko nekaj odpove na sredi.

Nenazadnje vedno preverite, ali komponente ekosistema, na katere se zanašate, resnično podpirajo asinhrono uporabo. Številne priljubljene knjižnice ponujajo ločene asinhrone odjemalce ali namenske podmodule; druge lahko še vedno blokirajo delovanje, tudi če oglašujejo »asinhrone« funkcije. Skrbno branje dokumentacije in izvajanje manjših primerjalnih testov vas lahko rešita pred subtilnimi regresijami zmogljivosti.

Praktični scenariji uporabe in arhitekturne ideje

V resničnih programskih projektih se asinhronost izkaže v različnih arhitekturah, od tradicionalnih spletnih zalednih sistemov do najsodobnejših sistemov, ki jih poganja umetna inteligenca. Združevalni element je vedno potreba po obravnavi številnih operacij, povezanih z V/I, ne da bi pri tem izgubljali čas s čakanjem v mirovanju.

Klasični scenarij je spletna storitev, ki mora poklicati več zunanjih API-jev, da zgradi en sam odgovor za odjemalca. Z uporabo asinhronosti lahko storitev sproži vse odhodne zahteve hkrati in sestavi končni koristni tovor takoj, ko vsak del prispe, kar znatno skrajša skupni odzivni čas. To je pogosto v arhitekturah mikrostoritev in integracijah s plačilnimi prehodi, družbenimi omrežji ali platformami za analitiko.

Drug pomemben primer uporabe je podatkovno inženirstvo: cevovodi in ETL opravila pogosto vzporedno komunicirajo z več bazami podatkov, datotečnimi sistemi ali vedri za shranjevanje v oblaku. Z branjem iz več virov hkrati in zapisovanjem rezultatov takoj, ko so pripravljeni, zmanjšate skupno zakasnitev in bolje izkoristite razpoložljivo pasovno širino, zlasti pri delu s shrambo v oblaku ali podatkovnimi API-ji, ki temeljijo na REST.

Async se odlično ujema tudi z nadzornimi ploščami za poslovno inteligenco in orodji, kot je Power BI, kjer morajo zaledni sistemi združevati podatke iz različnih storitev, ne da bi blokirali dolgotrajne povezave HTTP. Gradnja lastnih slojev API-ja ali integracijskih mikroservisov z asyncio lahko izboljša zaznano odzivnost in prepustnost pod obremenitvijo.

Podjetja, specializirana za programsko opremo po meri, umetno inteligenco, kibernetsko varnost in svetovanje v oblaku, se pogosto močno zanašajo na asinhrone tehnike za orkestriranje delovnih procesov, ki kličejo modele umetne inteligence, beležijo dogodke, spremljajo grožnje in komunicirajo z nadzornimi ravninami oblaka. Kombiniranje asinhronega V/I za orkestracijo z ločenimi delavci, optimiziranimi za CPU, za težko delo je pogost notranji vzorec, ki zagotavlja skalabilne in vzdrževalne sisteme.

Za številne razvijalce in ekipe je prvi korak preprosto uvedba asinhronosti v dele aplikacije, ki očitno kričijo »vezani na V/I«, nato pa od tam naprej ponavljanje, ko prednosti postanejo očitne in ekipa pridobi samozavest s paradigmami in orodji.

Konec koncev gre pri asinhronem programiranju v Pythonu za pametno uporabo čakalnega časa: s strukturiranjem kode okoli async, await, korutin in zanke dogodkov lahko zgradite aplikacije, ki se obnašajo hitreje, se bolje skalirajo pod obremenitvijo in kar najbolje izkoristijo razpoložljive vire, zlasti pri delu z omrežji, datotekami in zunanjimi storitvami.

vadnica za Node.js za principante
Povezani članek:
Vadnica za Node.js za principante: de cero a tu primera app
Podobni objav: