Avaa Pythonin rinnakkaisohjelmoinnin teho. Opi luomaan, hallitsemaan ja peruuttamaan Asyncio-tehtäviä rakentaaksesi suorituskykyisiä, skaalautuvia sovelluksia.
Python Asyncio: Tehtävien luonnin ja hallinnan syväsukellus
Nykyaikaisen ohjelmistokehityksen maailmassa suorituskyky on ensiarvoisen tärkeää. Sovellusten odotetaan olevan responsiivisia ja pystyvän käsittelemään tuhansia samanaikaisia verkkoyhteyksiä, tietokantakyselyitä ja API-kutsuja ilman ongelmia. I/O-sidonnaisissa operaatioissa – joissa ohjelma viettää suurimman osan ajastaan odottaen ulkoisia resursseja, kuten verkkoa tai levyä – perinteinen synkroninen koodi voi muodostua merkittäväksi pullonkaulaksi. Tässä asynkroninen ohjelmointi loistaa, ja Pythonin asyncio
-kirjasto on avain tämän tehon vapauttamiseen.
asyncio
:n rinnakkaisuusmallin ytimessä on yksinkertainen mutta tehokas käsite: Tehtävä (Task). Siinä missä korutiinit määrittelevät mitä tehdään, Tehtävät ovat niitä, jotka todella saavat asiat tehtyä. Ne ovat rinnakkaisen suorituksen perusyksikkö, jonka avulla Python-ohjelmasi voivat jongleerata useita operaatioita samanaikaisesti, parantaen dramaattisesti läpimenoa ja reagointikykyä.
Tämä kattava opas vie sinut syväsukellukseen asyncio.Task
-tehtävään. Käymme läpi kaiken perusteista, kuten luonnista, aina edistyneisiin hallintamalleihin, peruuttamiseen ja parhaisiin käytäntöihin. Rakensitpa sitten korkean liikenteen verkkopalvelua, datan kaapimisohjelmaa tai reaaliaikaista sovellusta, Tehtävien hallitseminen on välttämätön taito jokaiselle modernille Python-kehittäjälle.
Mikä on Korutiini? Nopea kertaus
Ennen kuin voimme juosta, meidän on käveltävä. Ja asyncio
:n maailmassa kävely tarkoittaa korutiinien ymmärtämistä. Korutiini on erityinen funktiotyyppi, joka määritellään async def
-syntaksilla.
Kun kutsut tavallista Python-funktiota, se suoritetaan alusta loppuun. Kun kutsut korutiinifunktiota, se ei kuitenkaan suorita itseään välittömästi. Sen sijaan se palauttaa korutiiniolion. Tämä olio on suunnitelma tehtävälle, mutta se on itsessään passiivinen. Se on pysäytetty laskenta, jota voidaan käynnistää, keskeyttää ja jatkaa.
import asyncio
async def say_hello(name: str):
print(f"Preparing to greet {name}...")
await asyncio.sleep(1) # Simuloi ei-blokkaavaa I/O-operaatiota
print(f"Hello, {name}!")
# Funktion kutsuminen ei suorita sitä, vaan luo korutiiniolion
coro = say_hello("World")
print(f"Created a coroutine object: {coro}")
# Suorittaaksesi sen todella, tarvitset aloituspisteen, kuten asyncio.run()
# asyncio.run(coro)
Maaginen sana on await
. Se kertoo tapahtumasilmukalle: "Tämä operaatio voi kestää kauan, joten keskeytä minut tässä ja mene tekemään jotain muuta. Herätä minut, kun tämä operaatio on valmis." Tämä kyky pysähtyä ja vaihtaa kontekstia mahdollistaa rinnakkaisuuden.
Rinnakkaisuuden ydin: asyncio.Task-tehtävän ymmärtäminen
Korutiini on siis suunnitelma. Kuinka kerromme keittiölle (tapahtumasilmukalle) aloittamaan ruoanlaiton? Tässä astuu kuvaan asyncio.Task
.
asyncio.Task
on olio, joka kietoo korutiinin ja aikatauluttaa sen suoritettavaksi asyncio-tapahtumasilmukassa. Ajattele sitä näin:
- Korutiini (
async def
): Yksityiskohtainen resepti annokselle. - Tapahtumasilmukka: Keskuskeittiö, jossa kaikki ruoanlaitto tapahtuu.
await my_coro()
: Seisot keittiössä ja seuraat itse reseptiä askel askeleelta. Et voi tehdä mitään muuta ennen kuin annos on valmis. Tämä on sekventiaalinen suoritus.asyncio.create_task(my_coro())
: Annat reseptin kokille (Tehtävä) keittiössä ja sanot: "Aloita tämän työstö." Kokki aloittaa välittömästi, ja sinulla on vapaus tehdä muita asioita, kuten antaa lisää reseptejä. Tämä on rinnakkainen suoritus.
Keskeinen ero on se, että asyncio.create_task()
aikatauluttaa korutiinin ajettavaksi "taustalla" ja palauttaa välittömästi ohjauksen koodillesi. Saat takaisin Task
-olion, joka toimii kahvana tälle meneillään olevalle operaatiolle. Voit käyttää tätä kahvaa tarkistaaksesi sen tilan, peruuttaaksesi sen tai odottaaksesi sen tulosta myöhemmin.
Ensimmäisten Tehtävien Luominen: `asyncio.create_task()`-funktio
Pääasiallinen tapa luoda Tehtävä on asyncio.create_task()
-funktio. Se ottaa argumenttinaan korutiiniolion ja aikatauluttaa sen suoritettavaksi.
Perussyntaksi
Käyttö on suoraviivaista:
import asyncio
async def my_background_work():
print("Starting background work...")
await asyncio.sleep(2)
print("Background work finished.")
return "Success"
async def main():
print("Main function started.")
# Aikatauluta my_background_work suoritettavaksi rinnakkain
task = asyncio.create_task(my_background_work())
# Tehtävän suorituksen aikana voimme tehdä muita asioita
print("Task created. Main function continues to run.")
await asyncio.sleep(1)
print("Main function did some other work.")
# Odota nyt, että tehtävä valmistuu ja hae sen tulos
result = await task
print(f"Task completed with result: {result}")
asyncio.run(main())
Huomaa, miten tuloste osoittaa, että `main`-funktio jatkaa suoritustaan välittömästi tehtävän luomisen jälkeen. Se ei esty. Se pysähtyy vasta, kun odotamme sitä eksplisiittisesti lopussa `await task`.
Käytännön Esimerkki: Rinnakkaiset verkkopyynnöt
Nähdään Tehtävien todellinen voima yleisessä skenaariossa: datan hakeminen useista URL-osoitteista. Tähän käytämme suosittua aiohttp
-kirjastoa, jonka voit asentaa komennolla `pip install aiohttp`.
Ensin, katsotaan sekventiaalinen (hidas) tapa:
import asyncio
import aiohttp
import time
async def fetch_status(session, url):
async with session.get(url) as response:
return response.status
async def main_sequential():
urls = [
"https://www.python.org",
"https://www.google.com",
"https://www.github.com",
"https://www.microsoft.com"
]
start_time = time.time()
async with aiohttp.ClientSession() as session:
for url in urls:
status = await fetch_status(session, url)
print(f"Status for {url}: {status}")
end_time = time.time()
print(f"Sequential execution took {end_time - start_time:.2f} seconds")
# Suorittaaksesi tämän, käyttäisit: asyncio.run(main_sequential())
Jos kukin pyyntö kestää noin 0.5 sekuntia, kokonaisaika on noin 2 sekuntia, koska jokainen `await` estää silmukan, kunnes yksittäinen pyyntö on valmis.
Nyt vapautetaan rinnakkaisuuden voima Tehtävillä:
import asyncio
import aiohttp
import time
# fetch_status korutiini pysyy samana
async def fetch_status(session, url):
async with session.get(url) as response:
return response.status
async def main_concurrent():
urls = [
"https://www.python.org",
"https://www.google.com",
"https://www.github.com",
"https://www.microsoft.com"
]
start_time = time.time()
async with aiohttp.ClientSession() as session:
# Luodaan tehtävien lista, mutta ei odoteta niitä vielä
tasks = [asyncio.create_task(fetch_status(session, url)) for url in urls]
# Odota nyt kaikkien tehtävien valmistumista
statuses = await asyncio.gather(*tasks)
for url, status in zip(urls, statuses):
print(f"Status for {url}: {status}")
end_time = time.time()
print(f"Concurrent execution took {end_time - start_time:.2f} seconds")
asyncio.run(main_concurrent())
Kun suoritat rinnakkaisen version, näet dramaattisen eron. Kokonaisaika on suunnilleen pisimmän yksittäisen pyynnön kesto, ei kaikkien pyyntöjen summa. Tämä johtuu siitä, että heti kun ensimmäinen `fetch_status`-korutiini saavuttaa `await session.get(url)`-kohdan, tapahtumasilmukka pysäyttää sen ja aloittaa välittömästi seuraavan. Kaikki verkkopyynnöt tapahtuvat tehokkaasti samanaikaisesti.
Tehtäväryhmän Hallinta: Keskeiset Mallit
Yksittäisten tehtävien luominen on hienoa, mutta todellisissa sovelluksissa sinun on usein käynnistettävä, hallittava ja synkronoitava kokonainen tehtäväryhmä. `asyncio` tarjoaa useita tehokkaita työkaluja tähän.
Moderni Lähestymistapa (Python 3.11+): `asyncio.TaskGroup`
Python 3.11:ssä esitelty `TaskGroup` on uusi, suositeltu ja turvallisin tapa hallita tehtävien ryhmää. Se tarjoaa niin kutsutun rakenteellisen rinnakkaisuuden.
TaskGroup
:n keskeiset ominaisuudet:
- Taattu siivous:
async with
-lohko ei poistu ennen kuin kaikki sen sisällä luodut tehtävät ovat valmistuneet. - Vankka virheidenkäsittely: Jos jokin ryhmän tehtävistä nostaa poikkeuksen, kaikki muut ryhmän tehtävät perutaan automaattisesti, ja poikkeus (tai
ExceptionGroup
) nostetaan uudelleenasync with
-lohkon poistuessa. Tämä estää orvoiksi jäävät tehtävät ja varmistaa ennustettavan tilan.
Näin sitä käytetään:
import asyncio
async def worker(delay):
print(f"Worker starting, will sleep for {delay}s")
await asyncio.sleep(delay)
# Tämä työntekijä epäonnistuu
if delay == 2:
raise ValueError("Something went wrong in worker 2")
print(f"Worker with delay {delay} finished")
return f"Result from {delay}s"
async def main():
print("Starting main with TaskGroup...")
try:
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(worker(1))
task2 = tg.create_task(worker(2)) # Tämä epäonnistuu
task3 = tg.create_task(worker(3))
print("Tasks created in the group.")
# Tämä osa koodista EI saavuteta, jos tapahtuu poikkeus
# Tulokset haettaisiin tehtävästä task1.result(), jne.
print("All tasks completed successfully.")
except* ValueError as eg: # Huomaa `except*` ExceptionGroupille
print(f"Caught an exception group with {len(eg.exceptions)} exceptions.")
for exc in eg.exceptions:
print(f" - {exc}")
print("Main function finished.")
asyncio.run(main())
Kun suoritat tämän, näet, että `worker(2)` nostaa virheen. `TaskGroup` sieppaa tämän, peruuttaa muut käynnissä olevat tehtävät (kuten `worker(3)`) ja nostaa sitten `ExceptionGroup`-olion, joka sisältää `ValueError`:in. Tämä malli on uskomattoman vankka luotettavien järjestelmien rakentamiseen.
Klassinen Työhevonen: `asyncio.gather()`
Ennen `TaskGroup`:ia `asyncio.gather()` oli yleisin tapa suorittaa useita odotettavia asioita rinnakkain ja odottaa niiden kaikkien valmistumista.
gather()` ottaa vastaan korutiinien tai Tehtävien sekvenssin, suorittaa ne kaikki ja palauttaa listan niiden tuloksista syötteiden järjestyksessä. Se on korkean tason, kätevä funktio yleiseen tapaukseen "suorita kaikki nämä asiat ja anna minulle kaikki tulokset".
import asyncio
async def fetch_data(source, delay):
print(f"Fetching from {source}...")
await asyncio.sleep(delay)
return {"source": source, "data": f"some data from {source}"}
async def main():
# gather voi ottaa korutiineja suoraan
results = await asyncio.gather(
fetch_data("API", 2),
fetch_data("Database", 3),
fetch_data("Cache", 1)
)
print(results)
asyncio.run(main())
Virheidenkäsittely `gather()`:lla: Oletusarvoisesti, jos jokin `gather()`:lle annetuista odotettavista nostaa poikkeuksen, `gather()` välittää kyseisen poikkeuksen välittömästi, ja muut käynnissä olevat tehtävät perutaan. Voit muuttaa tätä käyttäytymistä parametrilla `return_exceptions=True`. Tässä tilassa poikkeuksen sijaan se sijoitetaan tuloslistaan vastaavaan paikkaan.
# ... pääfunktion sisällä
results = await asyncio.gather(
fetch_data("API", 2),
asyncio.create_task(worker(1)), # Tämä nostaa ValueError-virheen
fetch_data("Cache", 1),
return_exceptions=True
)
# results sisältää sekoituksen onnistuneita tuloksia ja poikkeusolioita
print(results)
Hienojakoinen Ohjaus: `asyncio.wait()`
asyncio.wait()` on matalamman tason funktio, joka tarjoaa yksityiskohtaisempaa hallintaa tehtäväryhmälle. Toisin kuin `gather()`, se ei palauta tuloksia suoraan. Sen sijaan se palauttaa kaksi tehtäväjoukkoa: `done` (valmistuneet) ja `pending` (odotettavat).
Sen tehokkain ominaisuus on `return_when`-parametri, joka voi olla:
asyncio.ALL_COMPLETED
(oletus): Palautuu, kun kaikki tehtävät ovat valmiita.asyncio.FIRST_COMPLETED
: Palautuu heti, kun vähintään yksi tehtävä valmistuu.asyncio.FIRST_EXCEPTION
: Palautuu, kun tehtävä nostaa poikkeuksen. Jos mikään tehtävä ei nosta poikkeusta, se vastaaALL_COMPLETED
:ia.
Tämä on äärimmäisen hyödyllistä tilanteissa, kuten kysely useisiin redundantteihin tietolähteisiin ja ensimmäisen vastaavan käyttäminen:
import asyncio
async def query_source(name, delay):
await asyncio.sleep(delay)
return f"Result from {name}"
async def main():
tasks = [
asyncio.create_task(query_source("Fast Mirror", 0.5)),
asyncio.create_task(query_source("Slow Main DB", 2.0)),
asyncio.create_task(query_source("Geographic Replica", 0.8))
]
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
# Hae tulos valmistuneesta tehtävästä
first_result = done.pop().result()
print(f"Got first result: {first_result}")
# Meillä on nyt käynnissä olevia tehtäviä, jotka ovat edelleen meneillään. Niiden siivoaminen on olennaista!
print(f"Cancelling {len(pending)} pending tasks...")
for task in pending:
task.cancel()
# Odota peruutettuja tehtäviä, jotta ne voivat käsitellä peruutuksen
await asyncio.gather(*pending, return_exceptions=True)
print("Cleanup complete.")
asyncio.run(main())
TaskGroup vs. gather() vs. wait(): Milloin mitäkin käyttää?
- Käytä `asyncio.TaskGroup` (Python 3.11+) oletusvalintanasi. Sen rakenteellinen rinnakkaisuusmalli on turvallisempi, selkeämpi ja vähemmän virhealtis tehtäväryhmän hallinnassa, joka kuuluu yhteen loogiseen operaatioon.
- Käytä `asyncio.gather()` kun sinun on suoritettava ryhmä itsenäisiä tehtäviä ja haluat yksinkertaisesti listan niiden tuloksista. Se on edelleen erittäin hyödyllinen ja hieman tiiviimpi yksinkertaisissa tapauksissa, erityisesti Python-versioissa ennen 3.11.
- Käytä `asyncio.wait()` edistyneissä skenaarioissa, joissa tarvitset hienojakoista ohjausta valmistumisehtojen suhteen (esim. odotus ensimmäisestä tuloksesta) ja olet valmis hallitsemaan manuaalisesti jäljellä olevat odottavat tehtävät.
Tehtävän Elinkaari ja Hallinta
Kun Tehtävä on luotu, voit olla sen kanssa vuorovaikutuksessa käyttämällä Task
-olion metodeja.
Tehtävän Tilanteen Tarkistaminen
task.done()
: PalauttaaTrue
, jos tehtävä on valmistunut (joko onnistuneesti, poikkeuksella tai peruutuksen seurauksena).task.cancelled()
: PalautuuTrue
, jos tehtävä peruutettiin.task.exception()
: Jos tehtävä nosti poikkeuksen, tämä palauttaa poikkeusolion. Muuten se palauttaaNone
. Voit kutsua tätä vain sen jälkeen, kun tehtävä ondone()
.
Tulosten Haku
Pääasiallinen tapa saada tehtävän tulos on yksinkertaisesti `await task`. Jos tehtävä valmistui onnistuneesti, tämä palauttaa arvon. Jos se nosti poikkeuksen, `await task` nostaa kyseisen poikkeuksen uudelleen. Jos se peruutettiin, `await task` nostaa `CancelledError`-poikkeuksen.
Vaihtoehtoisesti, jos tiedät, että tehtävä on done()
, voit kutsua `task.result()`. Tämä toimii identtisesti kuin `await task` arvojen palauttamisen tai poikkeusten noston suhteen.
Peruuttamisen Taide
Kyky peruuttaa pitkään kestävät operaatiot sulavasti on kriittistä vankkojen sovellusten rakentamisessa. Saatat joutua peruuttamaan tehtävän aikakatkaisun, käyttäjäpyynnön tai virheen vuoksi jossain muussa järjestelmässä.
Peruutat tehtävän kutsumalla sen task.cancel()
-metodia. Tämä ei kuitenkaan pysäytä tehtävää välittömästi. Sen sijaan se aikatauluttaa CancelledError
-poikkeuksen heittämisen korutiinin sisälle seuraavassa await
-kohdassa. Tämä on ratkaiseva yksityiskohta. Se antaa korutiinille mahdollisuuden siivota ennen poistumista.
Hyvin käyttäytyvän korutiinin tulisi käsitellä tämä `CancelledError` sulavasti, yleensä käyttämällä `try...finally`-lohkoa varmistaakseen, että resurssit, kuten tiedostokahvat tai tietokantayhteydet, suljetaan.
import asyncio
async def resource_intensive_task():
print("Acquiring resource (e.g., opening a connection)...")
try:
for i in range(10):
print(f"Working... step {i+1}")
await asyncio.sleep(1) # Tämä on await-kohta, johon CancelledError voidaan injektoida
except asyncio.CancelledError:
print("Task was cancelled! Cleaning up...")
raise # On hyvää käytäntöä heittää CancelledError uudelleen
finally:
print("Releasing resource (e.g., closing connection). This always runs.")
async def main():
task = asyncio.create_task(resource_intensive_task())
# Annetaan sen suoriutua hetken
await asyncio.sleep(2.5)
print("Main decides to cancel the task.")
task.cancel()
try:
await task
except asyncio.CancelledError:
print("Main has confirmed the task was cancelled.")
asyncio.run(main())
finally
-lohkon suoritus taataan, mikä tekee siitä täydellisen paikan siivouslogiikalle.
Aikakatkaisujen Lisääminen `asyncio.timeout()`- ja `asyncio.wait_for()` -toiminnoilla
Manuaalinen nukkuminen ja peruuttaminen on työlästä. `asyncio` tarjoaa apuvälineitä tälle yleiselle mallille.
Python 3.11+:ssa `asyncio.timeout()`-kontekstinhallinta on suositeltu tapa:
async def long_running_operation():
await asyncio.sleep(10)
print("Operation finished")
async def main():
try:
async with asyncio.timeout(2): # Aseta 2 sekunnin aikakatkaisu
await long_running_operation()
except TimeoutError:
print("The operation timed out!")
asyncio.run(main())
Vanhemmissa Python-versioissa voit käyttää `asyncio.wait_for()`. Se toimii samankaltaisesti, mutta kietoo odotettavan funktion kutsun sisään:
async def main_legacy():
try:
await asyncio.wait_for(long_running_operation(), timeout=2)
except asyncio.TimeoutError:
print("The operation timed out!")
asyncio.run(main_legacy())
Molemmat työkalut toimivat peruuttamalla sisemmän tehtävän, kun aikaraja saavutetaan, ja nostamalla `TimeoutError`-poikkeuksen (joka on `CancelledError`-luokan aliluokka).
Yleisiä Sudenkuoppia ja Parhaita Käytäntöjä
Tehtävien kanssa työskentely on tehokasta, mutta on muutamia yleisiä ansoja, joita on vältettävä.
- Sudenkuoppa: "Laukaise ja unohda"-virhe. Tehtävän luominen `create_task`-toiminnolla ja sen koskaan odottaminen (tai hallitsijan, kuten `TaskGroup`, käyttäminen) on vaarallista. Jos tehtävä nostaa poikkeuksen, poikkeus voi hävitä hiljaisesti, ja ohjelmasi saattaa päättyä ennen kuin tehtävä edes saa työnsä valmiiksi. Ole aina vastuussa jokaisesta tehtävästä, joka on vastuussa tuloksen odottamisesta.
- Sudenkuoppa: `asyncio.run()`:n ja `create_task()`:n sekoittaminen. `asyncio.run(my_coro())` on pääasiallinen aloituspiste `asyncio`-ohjelman käynnistämiseksi. Se luo uuden tapahtumasilmukan ja suorittaa annetun korutiinin, kunnes se valmistuu. `asyncio.create_task(my_coro())` -toimintoa käytetään olemassa olevan ajossa olevan asynkronisen funktion sisällä rinnakkaisen suorituksen aikatauluttamiseksi.
- Paras käytäntö: Käytä `TaskGroup`:ia moderniin Pythoniin. Sen suunnittelumalli estää monia yleisiä virheitä, kuten unohtuneet tehtävät ja käsittelemättömät poikkeukset. Jos käytössäsi on Python 3.11 tai uudempi, tee siitä oletusvalintasi.
- Paras käytäntö: Nimeä tehtäväsi. Kun luot tehtävää, käytä `name`-parametria: `asyncio.create_task(my_coro(), name='DataProcessor-123')`. Tämä on korvaamaton virheenkorjauksessa. Kun luettelet kaikki käynnissä olevat tehtävät, mielekkäät nimet auttavat sinua ymmärtämään, mitä ohjelmasi tekee.
- Paras käytäntö: Varmista sulava sammutus. Kun sovelluksesi tarvitsee sammua, varmista, että sinulla on mekanismi kaikkien käynnissä olevien taustatehtävien peruuttamiseksi ja niiden odottamiseksi, että ne siivotaan asianmukaisesti.
Edistyneet Käsitteet: Vilkaisu Tulevaan
Virheenkorjaukseen ja tarkasteluun `asyncio` tarjoaa muutaman hyödyllisen funktion:
asyncio.current_task()
: PalauttaaTask
-olion koodille, joka suoritetaan parhaillaan.asyncio.all_tasks()
: Palauttaa joukon kaikkia tapahtumasilmukan tällä hetkellä hallinnoimiaTask
-olioita. Tämä on erinomainen virheenkorjaukseen, jotta voidaan nähdä, mitä on käynnissä.
Voit myös liittää tehtäviin valmistumiskutsuja käyttämällä `task.add_done_callback()`. Vaikka tämä voi olla hyödyllistä, se johtaa usein monimutkaisempaan, callback-tyyliseen koodirakenteeseen. Modernit lähestymistavat, jotka käyttävät `await`, `TaskGroup` tai `gather`, ovat yleensä suositeltavampia luettavuuden ja ylläpidettävyyden vuoksi.
Yhteenveto
asyncio.Task
on modernin Pythonin rinnakkaisuuden moottori. Ymmärtämällä, kuinka tehtäviä luodaan, hallitaan ja niiden elinkaarta käsitellään sulavasti, voit muuttaa I/O-sidonnaiset sovelluksesi hitaista, sekventiaalisista prosesseista erittäin tehokkaiksi, skaalautuviksi ja reagoiviksi järjestelmiksi.
Olemme käyneet läpi matkan korutiinin aikatauluttamisen peruskäsitteestä `create_task()`:lla monimutkaisten työnkulkujen orkestrointiin `TaskGroup`:lla, `gather()`:lla ja `wait()`:lla. Olemme myös tarkastelleet robustin ohjelmiston rakentamisen kriittistä merkitystä virheidenkäsittelyn, peruuttamisen ja aikakatkaisujen suhteen.
Asynkronisen ohjelmoinnin maailma on laaja, mutta Tehtävien hallitseminen on merkittävin askel, jonka voit ottaa. Aloita kokeilu. Muunna sekventiaalinen, I/O-sidonnainen osa sovelluksestasi käyttämään rinnakkaisia tehtäviä ja todista suorituskyvyn parannukset itse. Ota rinnakkaisuuden voima omaksesi, niin olet hyvin varustautunut rakentamaan seuraavan sukupolven korkean suorituskyvyn Python-sovelluksia.