Predstav si, že program:
Procesor stále musí čakať na vykonanie vstupno výstupnej operácie
data = download()
process(data)
➡️ Program stojí a čaká… nič iné nerobí.
Ak nie je k dispozícii vstup alebo výstup, tak sa venujem počítaniu v rámci toho istého procesu.
Vykonávanie viac úloh naraz v jednom procese:
Jedna úloha trá krátko a procesor prepína medzi úlohami:
task1() task2() task1() task1() task2()
Implementácia môže vyzerať takto, ak máme zoznam úloh:
while True: for each taska in tasks: if task.is_ready(): task.run()
Event loop je:
„Mozog async systému”
Spustenie:
asyncio.run(main())
Toto je syntaktický cukr — pod kapotou vytvorí a spustí event loop.
Manuálne vytvorenie:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
loop.close()
Používa sa pri:
Zjednodušíme si situáciu - Máme jeden súbor a jednu úlohu:
Zo súboru sa niekedy dá čítať, niekedy nie.
Udalosť znamená, že sa čítať dá,
Metóda is_ready() vráti True ak sa momentálne dá čítať, False ak sa momentálne nedá.
Metóda read() vráti všetky dáta ktoré sa momentálne dajú prečítať, potom skončí.
Metóda robí nejaký výpočet, ktorý trvá obmedzený čas.
V cykle:
buffer = [] while running: if file.is_ready(): data.append(file.read()) task.run(buffer)
Takto dosiahneme, pre program nečaká, ale ak sa dá, tak alebo číta dáta alebo počíta.
Čo ak máme viac rôznych úloh?
def corroutine(): memory = [] def process_buffer(data):
memory.add(data)
process(data)
return process_buffer
async defDefinuje asynchrónnu funkciu — coroutine.
async def hello():
print(“Ahoj”)
Funkcia definovaná pomocou async def sa nazýva coroutine.
Volanie coroutine nevráti výsledok, ale objekt coroutine:
<coroutine object hello at 0x...>
Pre vykonanie potrebuješ await:
await hello()
awaitPoužíva sa na „čakanie” na výsledok bez blokovania event loopu.
await asyncio.sleep(1)
➡️ počas čakania môže bežať iný kód.
asyncioHlavná knižnica pre async v Pythone.
import asyncio
async def task1():
print(“Task 1 začína”)
await asyncio.sleep(2)
print(“Task 1 končí”)
async def task2():
print(“Task 2 začína”)
await asyncio.sleep(1)
print(“Task 2 končí”)
async def main():
await task1()
await task2()
asyncio.run(main())
👉 Toto je stále sekvenčné (najprv task1, potom task2).
Použijeme asyncio.gather():
async def main():
await asyncio.gather(task1(), task2())
Veľmi dôležitý rozdiel.
Úloha A
Úloha B
Úloha A
Úloha B
Prepínanie medzi úlohami.
CPU 1 → Úloha A
CPU 2 → Úloha B
Skutočne súčasné vykonávanie.
👉 Async = concurrency
👉 Multiprocessing = parallelism
Task umožňuje spustiť coroutine na pozadí a mať nad ňou kontrolu.
task = asyncio.create_task(task1())
await task
asyncio.create_task() v praxiimport asyncio
async def work():
await asyncio.sleep(2)
print(“Hotovo”)
async def main():
task = asyncio.create_task(work())
print(“Robím niečo iné...”)
await task
asyncio.run(main())
Výstup:
Robím niečo iné...
Hotovo
Nikdy nechceme čakať večne.
import asyncio
async def slow():
await asyncio.sleep(10)
async def main():
try:
await asyncio.wait_for(slow(), timeout=3)
except asyncio.TimeoutError:
print(“Timeout!”)
asyncio.run(main())
Task možno zrušiť pomocou task.cancel().
import asyncio
async def worker():
try:
while True:
print(“Pracujem...”)
await asyncio.sleep(1)
except asyncio.CancelledError:
print(“Task zrušený”)
async def main():
task = asyncio.create_task(worker())
await asyncio.sleep(3)
task.cancel()
await task
asyncio.run(main())
Výnimky fungujú rovnako ako pri synchronnom kóde.
async def divide(a, b):
return a / b
async def main():
try:
result = await divide(10, 0)
except ZeroDivisionError:
print(“Delenie nulou”)
asyncio.run(main())
Predstav si, že API povoľuje len 5 requestov naraz.
import asyncio
sem = asyncio.Semaphore(5)
async def fetch(url):
async with sem:
await asyncio.sleep(1)
Výhoda:
Na komunikáciu medzi taskmi slúži asyncio.Queue.
import asyncio
async def producer(queue):
for i in range(5):
await queue.put(i)
async def consumer(queue):
while True:
item = await queue.get()
print(item)
queue.task_done()
async def main():
queue = asyncio.Queue()
asyncio.create_task(consumer(queue))
await producer(queue)
await queue.join()
asyncio.run(main())
Použitie:
Tak ako existuje:
with open(“file.txt”) as f:
existuje aj:
async with session.get(url) as response:
Používa sa pri objektoch vyžadujúcich asynchrónne otvorenie alebo zatvorenie.
Bežný iterátor:
for item in data:
...
Asynchrónny iterátor:
async for item in stream:
print(item)
Príklad:
async for line in websocket:
print(line)
Môžeš kombinovať yield a async.
async def generator():
for i in range(5):
await asyncio.sleep(1)
yield i
Použitie:
async for item in generator():
print(item)
Tento kód nebude rýchlejší:
async def calculate():
total = 0
for i in range(10**9):
total += i
Prečo?
Ak musíš použiť blokujúcu funkciu:
import asyncio
def blocking():
import time
time.sleep(5)
return 42
async def main():
result = await asyncio.to_thread(blocking)
print(result)
asyncio.run(main())
Výhoda:
import aiohttp
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
“https://example.com”,
“https://example.org”
]
results = await asyncio.gather(*(fetch(url) for url in urls))
print(len(results))
asyncio.run(main())
Moderné knižnice podporujú async:
import asyncpg
conn = await asyncpg.connect(...)
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine(DB_URL)
Typický async backend:
Klient
│
▼
FastAPI
│
├── HTTP requesty
├── Databáza
├── Redis
└── Message Queue
Všetko môže fungovať asynchrónne bez vytvárania tisícok vlákien.
Najznámejšie:
@app.get(“/”)
async def root():
return {“msg”: “hello”}
from aiohttp import web
from sanic import Sanic
awaittask1() # nič sa nespustí!
time.sleep(2) # ZLE
✔ správne:
await asyncio.sleep(2)
| Feature | Async | Threads |
|---|---|---|
| Výkon (I/O) | 🔥 vysoký | dobrý |
| Pamäť | nízka | vyššia |
| CPU úlohy | ❌ slabé | ✔ lepšie |
| Zložitosť | stredná | vyššia |
Predstav si čašníka:
Asynchrónne programovanie v Pythone je model založený na event loop-e, ktorý umožňuje efektívne spracovať veľké množstvo I/O operácií bez vytvárania veľkého počtu vlákien.
Zapamätaj si:
async def vytvára coroutine.await neblokuje program.asyncio.gather() spúšťa viac úloh súčasne.create_task() spúšťa úlohy na pozadí.Tu je minimálna implementácia vlastného event loopu — bez asyncio, len so select:
import select
import socket
class MiniEventLoop:
def __init__(self):
self.ready = [] # úlohy, ktoré sa môžu spustiť
self.waiting = {} # fd → úloha (čaká na I/O)
self.sock = socket.socket()
def create_task(self, coro):
self.ready.append(coro)
def add_reader(self, fd, coro, *args):
self.waiting[fd] = (coro, args)
def run(self):
while self.ready or self.waiting:
# 1) Spustíme pripravené úlohy
while self.ready:
coro = self.ready.pop(0)
try:
coro.send(None) # krok coroutiny
except StopIteration:
pass
# 2) Čakáme na I/O pomocou select()
if self.waiting:
fds = list(self.waiting.keys())
readable, _, _ = select.select(fds, [], [])
for fd in readable:
coro, args = self.waiting.pop(fd)
self.ready.append(coro)
loop = MiniEventLoop()
Čo to robí:
ready.select().ready.👉 To je podstatu celého event loopu — čakať na I/O a prepínať medzi úlohami.