Ohjelmointi

Aloita asynkronointi Pythonissa

Asynkroninen ohjelmointi tai asynkronoitu Lyhyesti sanottuna, se on monien nykyaikaisten kielten ominaisuus, jonka avulla ohjelma voi taitaa useita toimintoja odottamatta tai jäämättä jostakin niistä. Se on älykäs tapa käsitellä tehokkaasti tehtäviä, kuten verkko- tai tiedosto I / O, joissa suurin osa ohjelman ajasta kuluu tehtävän päättymiseen.

Harkitse web-kaavintasovellusta, joka avaa 100 verkkoyhteyttä. Voit avata yhden yhteyden, odottaa tuloksia, sitten avata seuraavan ja odottaa tuloksia, ja niin edelleen. Suurin osa ohjelman ajasta kuluu verkon vastauksen odottamiseen, ei todellisen työn tekemiseen.

Async antaa sinulle tehokkaamman tavan: Avaa kaikki 100 yhteyttä kerralla ja vaihda sitten jokaisen aktiivisen yhteyden välillä, kun ne palauttavat tuloksia. Jos yksi yhteys ei tuota tuloksia, vaihda seuraavaan ja niin edelleen, kunnes kaikki yhteydet ovat palauttaneet tietonsa.

Asynkronisyntaksi on nyt Pythonin vakio-ominaisuus, mutta pitkäaikaisilla Pythonistasilla, jotka ovat tottuneet tekemään yhtä asiaa kerrallaan, voi olla vaikeuksia kietoa päätään sen ympärille. Tässä artikkelissa tutkitaan, miten asynkroninen ohjelmointi toimii Pythonissa ja miten se otetaan käyttöön.

Huomaa, että jos haluat käyttää asynkronointia Pythonissa, kannattaa käyttää Python 3.7: ää tai Python 3.8: ta (viimeisin versio tämän kirjoituksen jälkeen). Käytämme Pythonin asynkronisyntaksia ja auttajatoimintoja, jotka on määritelty kyseisissä kieliversioissa.

Milloin asynkronista ohjelmointia käytetään

Paras aika asynkronointiin on yleensä, kun yrität tehdä työtä, jolla on seuraavat ominaisuudet:

  • Työn valmistuminen kestää kauan.
  • Viive tarkoittaa I / O (levy tai verkko) -toimintojen odottamista, ei laskentaa.
  • Työ käsittää monia I / O-operaatioita kerralla, tai yksi tai useampi I / O-toiminto tapahtuu, kun yrität myös suorittaa muita tehtäviä.

Asyncin avulla voit asettaa useita tehtäviä rinnakkain ja toistaa ne tehokkaasti estämättä sovelluksen loppua.

Joitakin esimerkkejä tehtävistä, jotka toimivat hyvin asynkronoinnin kanssa:

  • Verkon kaavinta yllä kuvatulla tavalla.
  • Verkkopalvelut (esim. Verkkopalvelin tai kehys).
  • Ohjelmat, jotka koordinoivat tuloksia useista lähteistä, joiden palauttaminen vie kauan aikaa (esimerkiksi samanaikaiset tietokantakyselyt).

On tärkeää huomata, että asynkroninen ohjelmointi eroaa monisäikeestä tai moniprosessoinnista. Asynkronointitoiminnot suoritetaan kaikki samassa säikeessä, mutta ne perääntyvät tarvittaessa toisilleen, mikä tekee asynkronoinnista tehokkaampaa kuin ketjutus tai moniprosessointi monenlaisiin tehtäviin. (Lisätietoja tästä alla.)

Python asynkronoituodottaa ja asyncio

Python lisäsi äskettäin kaksi avainsanaa, asynkronoitu ja odottaa, asynkronointitoimintojen luomiseen. Harkitse tätä komentosarjaa:

def get_server_status (server_addr) # Mahdollisesti pitkään jatkuva operaatio ... return server_status def server_ops () results = [] results.append (get_server_status ('addr1.server') results.append (get_server_status ('addr2.server') return tuloksia 

Saman komentosarjan asynkroninen versio - ei toimiva, riittää antamaan meille käsityksen syntaksin toiminnasta - saattaa näyttää tältä.

async def get_server_status (server_addr) # Mahdollisesti pitkään jatkuva operaatio ... return server_status async def server_ops () results = [] results.append (odota get_server_status ('addr1.server') results.append (odota get_server_status ('addr2). palvelin ') palauttaa tulokset 

Toiminnot etuliitteellä asynkronoitu avainsanasta tulee asynkronisia toimintoja, jotka tunnetaan myös nimellä korutiineja. Korutiinit käyttäytyvät eri tavalla kuin tavalliset toiminnot:

  • Coroutines voi käyttää toista avainsanaa, odottaa, jonka avulla korutiini voi odottaa tuloksia toisesta korutiinista estämättä. Kunnes tulokset palaavat odottaaed coroutine, Python vaihtaa vapaasti muiden käynnissä olevien coroutineiden välillä.
  • Coroutines voi vain soitetaan muilta asynkronoitu toimintoja. Jos juokset server_ops () tai get_server_status () sellaisenaan skriptin rungosta, et saa heidän tuloksiaan; saat Python coroutine -objektin, jota ei voida käyttää suoraan.

Joten jos emme voi soittaa asynkronoitu toimintoja ei-asynkronisista toiminnoista, emmekä voi suorittaa asynkronoitu toiminnot suoraan, miten niitä käytetään? Vastaus: Käyttämällä asyncio kirjasto, joka siltoja asynkronoitu ja loput Pythonista.

Python asynkronoituodottaa ja asyncio esimerkki

Tässä on esimerkki (jälleen kerran, ei toimiva, mutta havainnollistava) siitä, miten web-kaavintasovellus voidaan kirjoittaa asynkronoitu ja asyncio. Tämä komentosarja ottaa luettelon URL-osoitteista ja käyttää useita asynkronoitu toiminto ulkoisesta kirjastosta (read_from_site_async ()) ladata ne ja koota tulokset.

tuo asyncio web_scraping_library-tiedostosta tuonti read_from_site_async asynk. def main (url_list): return odota asyncio.gather (* [read_from_site_async (_) for _ in url_list]) urls = ['//site1.com'''//othersite.com', '//uutisivusto.com'] results = asyncio.run (main (urls)) print (results) 

Yllä olevassa esimerkissä käytämme kahta yleistä asyncio toiminnot:

  • asyncio.run () käytetään käynnistämään asynkronoitu toiminto koodin ei-asynkronisesta osasta ja käynnistää siten kaikki ohjelman asynkroniset toiminnot. (Näin juoksemme main ().)
  • asyncio.gather () vie yhden tai useamman asynkillä koristelun toiminnon (tässä tapauksessa useita read_from_site_async () hypoteettisesta verkkokaaviosta), suorittaa ne kaikki ja odottaa kaikkien tulosten saapumista.

Ajatus tässä on, aloitamme sitten kaikkien sivustojen lukutoiminnon kerralla kerätä tulokset niiden saapuessa (siten asyncio.gather ()). Emme odota minkään toiminnon päättymistä ennen kuin siirrymme seuraavaan.

Pythonin asynkronointisovellusten komponentit

Olemme jo maininneet, kuinka Python-asynkronointisovellukset käyttävät korutiineja pääainesosana asyncio kirjasto niiden ylläpitämiseksi. Muutama muu elementti on myös avain Pythonin asynkronisiin sovelluksiin:

Tapahtumasilmukat

asyncio kirjasto luo ja hallinnoi tapahtumasilmukat, mekanismit, jotka käyttävät korutiineja loppuun saakka. Vain yhden tapahtumasilmukan pitäisi olla käynnissä kerrallaan Python-prosessissa, vain jotta ohjelmoijan olisi helpompi seurata siihen liittyviä asioita.

Tehtävät

Kun lähetät korutiinin tapahtumasilmukkaan käsittelyä varten, voit saada takaisin a Tehtävä objekti, joka tarjoaa tavan hallita korutiinin käyttäytymistä tapahtumasilmukan ulkopuolelta. Jos sinun on esimerkiksi peruutettava käynnissä oleva tehtävä, voit tehdä sen soittamalla tehtävään .peruuttaa() menetelmä.

Tässä on hieman erilainen versio site-scraper-komentosarjasta, joka näyttää tapahtumasilmukan ja tehtävät työssä:

Tuo asynsio web_scraping_library-tiedostosta Tuo read_from_site_async-tehtävät = [] async def def (url_list): for n in url_list: task.append (asyncio.create_task (read_from_site_async (n))) tulosta (tehtävät) return odottaa asyncio.gather (* = ['//site1.com','//othersite.com','//newsite.com'] loop = asyncio.get_event_loop () results = loop.run_until_complete (main (URL)) tulosta (tulokset) 

Tämä komentosarja käyttää tapahtumasilmukkaa ja tehtäväobjekteja tarkemmin.

  • .get_event_loop () menetelmä tarjoaa meille objektin, jonka avulla voimme hallita tapahtumasilmukkaa suoraan lähettämällä asynkroniset toiminnot sille ohjelmallisesti .run_until_complete (). Edellisessä komentosarjassa voimme suorittaa vain yhden ylätason asynkronointitoiminnon käyttämällä asyncio.run (). Muuten, .run_until_complete () tekee tarkalleen mitä se sanoo: Se suorittaa kaikki toimitetut tehtävät, kunnes ne on tehty, ja palauttaa sitten tulokset yhtenä eränä.
  • .create_task () method vie toiminnon suoritettavaksi, mukaan lukien sen parametrit, ja antaa meille takaisin a Tehtävä objekti ajaa se. Tässä lähetämme kukin URL-osoitteet erillisinä Tehtävä tapahtumasilmukkaan ja tallenna Tehtävä objektit luettelossa. Huomaa, että voimme tehdä tämän vain tapahtumasilmukan sisällä, ts asynkronoitu toiminto.

Kuinka paljon hallintaa tarvitset tapahtumasilmukkaan ja sen tehtäviin, riippuu siitä, kuinka monimutkainen sovellus on rakentamasi. Jos haluat vain lähettää kiinteitä töitä samanaikaisesti suoritettavaksi, kuten verkkokaavereissamme, et tarvitse paljon hallintaa - vain tarpeeksi töiden käynnistämiseen ja tulosten keräämiseen.

Sitä vastoin, jos luot täysimittaisen verkkokehyksen, haluat paljon paremman hallinnan korutiinien ja tapahtumasilmukan käyttäytymisessä. Saatat joutua esimerkiksi sulkemaan tapahtumasilmukan sulavasti sovelluksen kaatumisen yhteydessä tai suorittamaan tehtäviä langattomasti, jos soitat tapahtumasilmukkaa toisesta säikeestä.

Asynkronointi vs. langoittaminen vs. moniprosessointi

Tässä vaiheessa saatat miettiä, miksi käyttää asynkronointia ketjujen tai moniprosessoinnin sijaan, jotka molemmat ovat olleet pitkään saatavilla Pythonissa?

Ensinnäkin asynkronoinnin ja ketjujen tai moniprosessoinnin välillä on keskeinen ero, riippumatta siitä, kuinka nuo asiat toteutetaan Pythonissa. Async on noin samanaikaisuus, kun taas ketjut ja moniprosessointi ovat noin rinnakkaisuus. Samanaikaisuus tarkoittaa ajan jakamista tehokkaasti useiden tehtävien välillä kerralla - esimerkiksi tarkistamalla sähköpostiosoitteesi odottaessasi rekisteriä ruokakaupassa. Rinnakkaisuuteen liittyy useita agentteja, jotka käsittelevät useita tehtäviä vierekkäin - esimerkiksi, kun ruokakaupassa on avoinna viisi erillistä rekisteriä.

Async on suurimmaksi osaksi hyvä ketjutuksen korvike, koska ketjutetaan Pythonissa. Tämä johtuu siitä, että Python ei käytä käyttöjärjestelmän ketjuja, vaan omia yhteistyöketjuja, joissa tulkkissa on aina vain yksi ketju kerrallaan. Async tarjoaa yhteistyökierteisiin verrattuna joitain keskeisiä etuja:

  • Asynkronitoiminnot ovat paljon kevyempiä kuin langat. Kymmenillä tuhansilla samanaikaisesti käynnissä olevilla asynkronisilla operaatioilla on paljon vähemmän yleiskustannuksia kuin kymmenillä tuhansilla säikeillä.
  • Asynkronikoodin rakenteen avulla on helpompi päättää, mistä tehtävät nousevat ja loppuvat. Tämä tarkoittaa, että tietokilpailuissa ja langan turvallisuudessa ei ole merkitystä. Koska kaikki asynkronisen tapahtumasilmukan tehtävät suoritetaan yhdessä ketjussa, Python (ja kehittäjä) on helpompi sarjoittaa, miten ne käyttävät muistissa olevia objekteja.
  • Asynkronointitoiminnot voidaan peruuttaa ja käsitellä helpommin kuin ketjut. Tehtävä esine, josta palaamme asyncio.create_task () tarjoaa meille kätevän tavan tehdä tämä.

Toisaalta moniprosessointi Pythonissa on parasta töille, jotka ovat voimakkaasti suorittimen eikä I / O-sidottuja. Async toimii itse asiassa käsi kädessä moniprosessoinnin kanssa, kuten voit käyttää asyncio.run_in_executor () siirtää CPU-intensiivisiä töitä prosessiryhmään keskitetystä prosessista estämättä sitä.

Seuraavat vaiheet Python-asynkronoinnilla

Paras tehtävä on rakentaa muutama yksinkertainen oma asynkronointisovellus. Hyviä esimerkkejä on nyt runsaasti, kun Pythonin asynkroninen ohjelmointi on käynyt läpi muutaman version, ja sillä on ollut pari vuotta aikaa asettua alas ja tulla yleisemmäksi. Viralliset asiakirjat asyncio on lukemisen arvoinen nähdäksesi, mitä se tarjoaa, vaikka et aio käyttää kaikkia sen toimintoja.

Saatat myös tutkia asynkronoitujen kirjastojen ja väliohjelmistojen kasvavaa määrää, joista monet tarjoavat asynkronisia, estämättömiä versioita tietokantaliittimistä, verkkoprotokollista ja vastaavista. aio-libs arkistossa on joitain keskeisiä, kuten aiohittp kirjasto verkkoon pääsyä varten. On myös syytä etsiä kirjastoja Python Package Index -hakemistosta asynkronoitu avainsana. Asynkronisen ohjelmoinnin kaltaisella tavalla paras tapa oppia on nähdä, miten muut ovat panneet sen käyttämään.

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