Programmering

Kom i gang med asynkronisering i Python

Asynkron programmering, eller asynkronisering for kort, er en funksjon av mange moderne språk som lar et program sjonglere med flere operasjoner uten å vente eller bli hengt opp på noen av dem. Det er en smart måte å effektivt håndtere oppgaver som nettverk eller fil-I / U, hvor det meste av programmets tid blir brukt på å vente på at en oppgave skal fullføres.

Vurder et nettskrapeapplikasjon som åpner 100 nettverkstilkoblinger. Du kan åpne en forbindelse, vente på resultatene, deretter åpne neste og vente på resultatene, og så videre. Mesteparten av tiden programmet kjører, blir brukt på å vente på et nettverksrespons, og ikke utføre faktisk arbeid.

Async gir deg en mer effektiv metode: Åpne alle 100 tilkoblinger samtidig, og bytt deretter mellom hver aktive tilkobling når de gir resultater. Hvis en tilkobling ikke gir resultater, bytter du til den neste og så videre, til alle tilkoblinger har returnert dataene.

Async-syntaks er nå en standard funksjon i Python, men mangeårige Pythonistas som er vant til å gjøre en ting av gangen, kan ha problemer med å pakke hodet rundt det. I denne artikkelen vil vi undersøke hvordan asynkron programmering fungerer i Python, og hvordan du bruker den.

Merk at hvis du vil bruke asynkronisering i Python, er det best å bruke Python 3.7 eller Python 3.8 (den siste versjonen fra og med dette skrivet). Vi bruker Pythons asynk-syntaks og hjelperfunksjoner som definert i de versjonene av språket.

Når skal du bruke asynkron programmering

Generelt sett er de beste tidene å bruke asynkronisering når du prøver å utføre arbeid som har følgende egenskaper:

  • Arbeidet tar lang tid å fullføre.
  • Forsinkelsen innebærer å vente på I / O (disk eller nettverk) -operasjoner, ikke beregning.
  • Arbeidet involverer mange I / O-operasjoner på en gang, eller en eller flere I / O-operasjoner som skjer når du også prøver å få gjort andre oppgaver.

Med Async kan du konfigurere flere oppgaver parallelt og gjenta dem effektivt, uten å blokkere resten av applikasjonen.

Noen eksempler på oppgaver som fungerer bra med async:

  • Nettskraping, som beskrevet ovenfor.
  • Nettverkstjenester (f.eks. En webserver eller et rammeverk).
  • Programmer som koordinerer resultater fra flere kilder som det tar lang tid å returnere verdier (for eksempel samtidige databasespørsmål).

Det er viktig å merke seg at asynkron programmering er forskjellig fra flertråding eller flerbehandling. Async-operasjoner kjører alle i samme tråd, men de gir hverandre etter behov, noe som gjør async mer effektiv enn å tre eller multiprosessere for mange slags oppgaver. (Mer om dette nedenfor.)

Python asynkroniseringavvente og asyncio

Python la nylig til to nøkkelord, asynkronisering og avvente, for å opprette asynkroniseringsoperasjoner. Tenk på dette skriptet:

def get_server_status (server_addr) # En potensielt langvarig operasjon ... return server_status def server_ops () results = [] results.append (get_server_status ('addr1.server') results.append (get_server_status ('addr2.server') return resultater 

En asynkronisert versjon av det samme skriptet - ikke funksjonell, akkurat nok til å gi oss en ide om hvordan syntaksen fungerer - kan se slik ut.

async def get_server_status (server_addr) # En potensielt langvarig operasjon ... return server_status async def server_ops () results = [] results.append (await get_server_status ('addr1.server') results.append (await get_server_status ('addr2. server ') returnerer resultater 

Funksjoner prefikset med asynkronisering nøkkelord blir asynkrone funksjoner, også kjent som coroutines. Coroutines oppfører seg annerledes enn vanlige funksjoner:

  • Coroutines kan bruke et annet nøkkelord, avvente, som gjør at en coroutine kan vente på resultater fra en annen coroutine uten å blokkere. Inntil resultatene kommer tilbake fra avventeed coroutine, bytter Python fritt mellom andre kjørende coroutines.
  • Coroutines kan kun bli kalt fra andre asynkronisering funksjoner. Hvis du løper server_ops () eller get_server_status () som det er fra selve skriptet, får du ikke resultatene deres; du får et Python coroutine-objekt, som ikke kan brukes direkte.

Så hvis vi ikke kan ringe asynkronisering funksjoner fra ikke-asynkrone funksjoner, og vi kan ikke kjøre asynkronisering fungerer direkte, hvordan bruker vi dem? Svar: Ved å bruke asyncio bibliotek, som bygger bro asynkronisering og resten av Python.

Python asynkroniseringavvente og asyncio eksempel

Her er et eksempel (igjen, ikke funksjonelt, men illustrativt) på hvordan man kan skrive et webskrapeapplikasjon ved hjelp av asynkronisering og asyncio. Dette skriptet tar en liste over nettadresser og bruker flere forekomster av en asynkronisering funksjon fra et eksternt bibliotek (read_from_site_async ()) for å laste dem ned og samle resultatene.

importer asyncio fra web_scraping_library import read_from_site_async async def main (url_list): return await asyncio.gather (* [read_from_site_async (_) for _ in url_list]) urls = ['//site1.com','//othersite.com', '//newsite.com'] results = asyncio.run (main (urls)) print (results) 

I eksemplet ovenfor bruker vi to vanlige asyncio funksjoner:

  • asyncio.run () brukes til å starte en asynkronisering fungerer fra den ikke-asynkrone delen av koden vår, og dermed sparker i gang alle progamets asynkroniseringsaktiviteter. (Dette er hvordan vi løper hoved().)
  • asyncio.gather () tar en eller flere asynkroniserte funksjoner (i dette tilfellet flere forekomster av read_from_site_async () fra vårt hypotetiske nett-skrapebibliotek), kjører dem alle og venter på at alle resultatene skal komme inn.

Tanken her er at vi starter leseoperasjonen for alle nettstedene samtidig samle resultatene når de kommer (derav asyncio.gather ()). Vi venter ikke på at en operasjon skal fullføres før vi går videre til den neste.

Komponenter av Python async-apper

Vi har allerede nevnt hvordan Python async-apper bruker coroutines som hovedingrediens, basert på asyncio biblioteket for å kjøre dem. Noen få andre elementer er også nøkkelen til asynkrone applikasjoner i Python:

Arrangementsløkker

De asyncio biblioteket oppretter og administrerer begivenhetsløkker, mekanismene som kjører coroutines til de fullføres. Bare en hendelsessløyfe skal kjøres om gangen i en Python-prosess, om ikke bare for å gjøre det lettere for programmereren å holde oversikt over hva som går inn i den.

Oppgaver

Når du sender en coroutine til en hendelsessløyfe for behandling, kan du få tilbake en Oppgave objekt, som gir en måte å kontrollere oppførselen til coroutine utenfor hendelsessløyfen. Hvis du for eksempel må avbryte den løpende oppgaven, kan du gjøre det ved å ringe oppgaven .Avbryt() metode.

Her er en litt annen versjon av site-scraper-skriptet som viser hendelsessløyfen og oppgavene på jobben:

importer asyncio fra web_scraping_library import read_from_site_async oppgaver = [] async def main (url_list): for n i url_list: Tasks.append (asyncio.create_task (read_from_site_async (n))) skriv ut (oppgaver) return vent på asyncio.gather = ['//site1.com','//othersite.com','//newsite.com'] loop = asyncio.get_event_loop () results = loop.run_until_complete (main (urls)) print (results) 

Dette skriptet bruker hendelsessløyfen og oppgaveobjektene mer eksplisitt.

  • De .get_event_loop () metoden gir oss et objekt som lar oss kontrollere hendelsessløyfen direkte, ved å sende async-funksjoner til den programmatisk via .run_until_complete (). I det forrige skriptet kunne vi bare kjøre en enkelt async-funksjon på toppnivå ved hjelp av asyncio.run (). Forresten, .run_until_complete () gjør nøyaktig hva det står: Den kjører alle oppgavene som følger med til de er ferdige, og returnerer deretter resultatene i en enkelt batch.
  • De .create_task () metoden tar en funksjon å kjøre, inkludert parametrene, og gir oss tilbake en Oppgave innvending for å kjøre den. Her sender vi hver URL som en separat Oppgave til hendelsessløyfen, og lagre Oppgave objekter i en liste. Merk at vi bare kan gjøre dette i hendelsessløyfen - det vil si inne i en asynkronisering funksjon.

Hvor mye kontroll du trenger over hendelsessløyfen og dens oppgaver, vil avhenge av hvor kompleks applikasjonen du bygger. Hvis du bare vil sende inn et sett med faste jobber som skal kjøres samtidig, som med nettskrapen vår, trenger du ikke mye kontroll - akkurat nok til å starte jobber og samle resultatene.

Hvis du derimot oppretter et fullverdig web-rammeverk, vil du ønske deg mye mer kontroll over atferdene til coroutines og event loop. For eksempel kan det hende du må slå av hendelsessløyfen elegant i tilfelle et programkrasj, eller kjøre oppgaver på en trådsikker måte hvis du ringer til hendelsessløyfen fra en annen tråd.

Asynkronisering vs tråding vs. flerbehandling

På dette punktet lurer du kanskje på, hvorfor bruke async i stedet for tråder eller multiprosessering, som begge har vært lenge tilgjengelige i Python?

For det første er det en nøkkelforskjell mellom asynkronisering og tråder eller flerbehandling, selv bortsett fra hvordan disse tingene er implementert i Python. Async handler om samtidighet, mens tråder og flerbehandling er omtrent parallellitet. Samtidighet innebærer å dele tid effektivt mellom flere oppgaver samtidig - for eksempel å sjekke e-posten din mens du venter på et register i matbutikken. Parallelisme innebærer at flere agenter behandler flere oppgaver side om side - for eksempel å ha fem separate registre åpne i matbutikken.

Mesteparten av tiden er async en god erstatning for threading ettersom threading er implementert i Python. Dette er fordi Python ikke bruker OS-tråder, men sine egne samarbeidstråder, der bare en tråd noen gang kjører om gangen i tolken. Sammenlignet med samarbeidstråder gir async noen viktige fordeler:

  • Async-funksjoner er langt mer lette enn tråder. Titusenvis av asynkrone operasjoner som kjører samtidig, vil ha langt mindre overhead enn titusenvis av tråder.
  • Strukturen til asynk-kode gjør det lettere å resonnere om hvor oppgaver henter og slutter. Dette betyr at dataspill og trådsikkerhet er mindre av et problem. Fordi alle oppgaver i den asynkroniserte hendelsessløyfen går i en enkelt tråd, er det lettere for Python (og utvikleren) å serieisere hvordan de får tilgang til objekter i minnet.
  • Async-operasjoner kan avbrytes og manipuleres lettere enn tråder. De Oppgave objekt vi kommer tilbake fra asyncio.create_task () gir oss en praktisk måte å gjøre dette på.

Multiprosessering i Python er derimot best for jobber som er sterkt CPU-bundet i stedet for I / O-bundet. Async fungerer faktisk hånd i hånd med multiprosessering, slik du kan bruke asyncio.run_in_executor () å delegere CPU-intensive jobber til en prosessbasseng fra en sentral prosess, uten å blokkere den sentrale prosessen.

Neste trinn med Python async

Den beste første tingen å gjøre er å lage noen få, enkle asynkroniseringsprogrammer. Gode ​​eksempler florerer nå når asynkron programmering i Python har gjennomgått noen versjoner og hatt et par år på å slå seg til ro og bli mer brukt. Den offisielle dokumentasjonen for asyncio er verdt å lese gjennom for å se hva den tilbyr, selv om du ikke har tenkt å bruke alle funksjonene.

Du kan også utforske det økende antall asynkroniserte biblioteker og mellomvare, hvorav mange tilbyr asynkrone, ikke-blokkerende versjoner av databasekontakter, nettverksprotokoller og lignende. De aio-libs repository har noen viktige, for eksempel aiohittp bibliotek for nettilgang. Det er også verdt å søke i Python Package Index for biblioteker med asynkronisering nøkkelord. Med noe som asynkron programmering er den beste måten å lære å se hvordan andre har brukt den.

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