Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Hodisa sikli nima
- 2.2. Kirish nuqtasi
- 2.3. Task — fon vazifasi
- 2.4. Ko'p vazifani birga bajarish
- 2.5. Bekor qilish
- 2.6. Loopni bloklash
- 2.7. Navbat va tartib
- 2.8. Platforma, uvloop va zamonaviy CPython evolyutsiyasi
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Hodisa sikli ichidan
- Misol 2 — gather, TaskGroup, bekor qilish
- Misol 3 — Loopni bloklash va yechimlari
- Misol 4 — Amaliy: 500 ta ulanishli xizmat
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
14.6-dars: asyncio — hodisa sikli
14-QISM — KONKURENTLIK VA PARALLELLIK · 6-dars
1. Kirish va motivatsiya
Bitta savol bilan boshlaymiz: 10 000 ta ochiq ulanishni bir vaqtda qanday ushlab turasiz?
Iplar bilan — 10 000 ip kerak: har biriga kamida 8 KB stek, kontekst almashtirish, operatsion tizim chegarasi. Jarayonlar bilan — umuman imkonsiz. Lekin bu ulanishlarning 99% i hech narsa qilmayapti: ular ma'lumot kutayapti.
asyncio shu haqiqatdan foydalanadi: kutayotgan vazifa protsessorni ushlab turmasligi kerak. Bitta ipda bitta hodisa sikli (event loop) minglab vazifani navbat bilan bajaradi — har biri await ga kelganda boshqasiga yo'l beradi.
Real vaziyat. Chat serveri 800 ta bir vaqtdagi ulanishda "ip yaratib bo'lmadi" xatosi bilan yiqilardi. asyncio ga o'tkazilgandan keyin o'sha server bitta jarayonda 30 000 ulanishni ushlab turdi — chunki ulanishning ip emas, atigi bir necha kilobaytlik obyekt bo'lib qoldi.
Bu darsda mexanizmni ichidan ko'ramiz:
- Hodisa sikli qanday ishlaydi: navbat,
call_soon,awaitnuqtalari - Korutina obyekti,
Task,Future— farqi nimada -
gather,TaskGroup,as_completed,wait - Bekor qilish va
CancelledError - Loopni bloklash — eng ko'p uchraydigan xato
to_thread,run_in_executor— bloklovchi kodni ko'chirish- Nosozlik izlash:
asyncio.run(..., debug=True)
2. Nazariya — chuqur tushuntirish
2.1. Hodisa sikli nima
Hodisa sikli — bu cheksiz tsikl: tayyor vazifalarni navbatdan olib, ularni await ga duch kelguncha bajaradi.
while ishlayapti:
1. Tayyor navbatdagi har bir callback ni bajar
2. Tayyor bo'lgan I/O ni tekshir (select/epoll/IOCP)
3. Vaqti kelgan taymerlarni tayyor navbatga ko'chir| Tushuncha | Ma'nosi |
|---|---|
| Korutina funksiya | async def bilan e'lon qilingan funksiya |
| Korutina obyekti | Uni chaqirganda hosil bo'ladigan obyekt — hali bajarilmaydi |
Task |
Loop rejalashtirgan korutina (Future merosxo'ri) |
Future |
"Natija keyinroq bo'ladi" degan va'da |
await |
Nazoratni loopga qaytarish nuqtasi |
Korutinani chaqirish uni ishga tushirmaydi. Ishga tushirish uchun await yoki create_task kerak.
k = ishla() # hech narsa bajarilmadi
natija = await k # endi bajariladi
vazifa = asyncio.create_task(ishla()) # fon rejimida rejalashtirildi2.2. Kirish nuqtasi
asyncio.run(main()) # ⭐ yagona to'g'ri kirish nuqtasi
asyncio.run(main(), debug=True) # nosozlik izlash rejimiasyncio.run: yangi loop yaratadi → main() ni bajaradi → qolgan vazifalarni bekor qiladi → loopni yopadi.
| Funksiya | Qachon |
|---|---|
asyncio.run(main()) |
Dasturning kirish nuqtasi |
asyncio.get_running_loop() |
Korutina ichida loopga kirish |
asyncio.get_event_loop() |
Eskirgan uslub — ishlatmang |
loop.run_until_complete() |
Faqat maxsus holatlarda |
Loopdan tashqarida get_running_loop() → RuntimeError: no running event loop.
2.3. Task — fon vazifasi
vazifa = asyncio.create_task(ishla(), name="yuklovchi")
vazifa.done() / vazifa.result() / vazifa.cancel() / vazifa.get_name()
natija = await vazifa| Xususiyat | Izoh |
|---|---|
| Yaratilganda | Navbatga qo'yiladi, keyingi await da boshlanadi |
Future merosxo'ri |
isinstance(task, asyncio.Future) → True |
| Havolasiz qolsa | Yig'ib tashlanishi mumkin — havolani saqlang |
| Istisno o'qilmasa | "Task exception was never retrieved" ogohlantirishi |
Eager task factory (3.12+): loop.set_task_factory(asyncio.eager_task_factory) — vazifa yaratilishi bilanoq birinchi await gacha bajariladi (tezroq, lekin tartib o'zgaradi).
2.4. Ko'p vazifani birga bajarish
| Vosita | Xususiyati |
|---|---|
await asyncio.gather(*k) |
Hammasini kutadi; natijalar kirish tartibida |
gather(..., return_exceptions=True) |
Xatolar natija sifatida qaytadi |
async with asyncio.TaskGroup() |
3.11+: bittasi yiqilsa — qolganlari bekor qilinadi |
asyncio.as_completed(k) |
Tugash tartibida |
await asyncio.wait(k, timeout=) |
(tugagan, qolgan) to'plamlari |
async with asyncio.TaskGroup() as guruh: # ⭐ zamonaviy tanlov
guruh.create_task(birinchi())
guruh.create_task(ikkinchi())
# bu yerda ikkalasi ham tugagan; xato bo'lsa — ExceptionGroupTaskGroup xatolarni ExceptionGroup ga yig'adi va except* bilan ushlanadi (9.4-dars).
2.5. Bekor qilish
vazifa.cancel()
try:
await vazifa
except asyncio.CancelledError:
...| Fakt | Izoh |
|---|---|
CancelledError — BaseException |
except Exception uni ushlamaydi |
Bekor qilish await nuqtasida yetadi |
Bloklangan kod bekor qilinmaydi |
finally bajariladi |
Tozalash uchun ishonchli joy |
asyncio.shield(k) |
Bekor qilishdan himoya qiladi |
Muddatlar:
async with asyncio.timeout(5): # ⭐ 3.11+
await sekin()
natija = await asyncio.wait_for(sekin(), timeout=5)Ikkalasi ham TimeoutError beradi va vazifani haqiqatan bekor qiladi — bu concurrent.futures dagi muddatdan muhim farq (14.5-dars).
2.6. Loopni bloklash
Eng ko'p uchraydigan asyncio xatosi. Bitta ipda hamma vazifa bajariladi — bitta bloklovchi chaqiruv hammasini to'xtatadi.
| Bloklovchi | O'rniga |
|---|---|
time.sleep(1) |
await asyncio.sleep(1) |
requests.get(...) |
aiohttp / httpx.AsyncClient |
open(...).read() — katta fayl |
await asyncio.to_thread(...) |
| Og'ir hisob (sof Python) | run_in_executor(ProcessPoolExecutor(), ...) |
input(), psycopg2 va boshqalar |
Asinxron variant yoki to_thread |
natija = await asyncio.to_thread(bloklovchi_funksiya, arg) # ⭐ sodda yo'l
natija = await loop.run_in_executor(jarayonlar_puli, ogir, arg) # CPU uchun2.7. Navbat va tartib
loop.call_soon(f, arg) # keyingi aylanishda
loop.call_later(0.5, f) # 0.5 s dan keyin
await asyncio.sleep(0) # ⭐ navbatni boshqalarga berish await asyncio.sleep(0) — nazoratni loopga qaytarishning eng arzon usuli: uzoq tsikl ichida boshqa vazifalarga yo'l ochadi.
2.8. Platforma, uvloop va zamonaviy CPython evolyutsiyasi
| Platforma | Standart loop | Imkoniyatlar |
|---|---|---|
| Linux/macOS | SelectorEventLoop (epoll/kqueue) |
Standart, toza Python/C aralash |
| Windows | ProactorEventLoop (IOCP) |
Windows I/O Completion Ports |
| Yuqori tezlik (ishlab chiqarish) | uvloop (Linux/macOS) |
libuv (Node.js yadrosi) ustiga Cython'da qurilgan |
1. uvloop — 2x-4x tezkor hodisa sikli
Standart asyncio sikli sof Pythonda yozilgan qismlarga ega. uvloop esa asyncio.AbstractEventLoop interfeysini to'liq qamrab olgan va Cython'da yozilgan. U Python asinxron tarmoq tezligini Go va Node.js darajasiga yaqinlashtiradi.
# O'rnatish: pip install uvloop
import asyncio
import uvloop
# 1-usul: Python 3.11+ da loop_factory orqali
asyncio.run(main(), loop_factory=uvloop.new_event_loop)
# 2-usul: Global hodisa sikli siyosatini o'rnatish
uvloop.install()
asyncio.run(main())
# 3-usul: FastAPI / Uvicorn bilan ishlab chiqarishda:
# uvicorn main:app --loop uvloop --http httptools2. Python 3.12 va 3.13: GIL inqilobi
Konkurentlik va parallellik tarixida Python 3.12/3.13 eng katta burilish nuqtasi bo'ldi:
- Free-threaded CPython (PEP 703 — Python 3.13): Python 3.13 dan boshlab GIL'ni butunlay o'chirib ishlatish mumkin (
python3.13tyoki--disable-gil). Bu rejimdathreading.Threadorqali CPU-bound hisob-kitoblar bir jarayon ichida bir nechta CPU yadrolarida haqiqiy parallel ishlaydi. - Per-Interpreter GIL va Subinterpreters (PEP 684 / PEP 554): Bitta Python jarayoni ichida bir nechta mustaqil sub-interpreterlar yaratiladi va har biri o'zining shaxsiy GIL'iga ega bo'ladi. Ular xotirani bo'lishmaydi, IPC (jarayonlararo aloqa) xarajatisiz haqiqiy parallellik beradi.
asyncio.run(main(), debug=True) # sekin callbacklar va unutilgan korutinalar haqida ogohlantiradi3. Tez ma'lumotnoma
import asyncio
async def main() -> None:
async with asyncio.TaskGroup() as guruh:
guruh.create_task(ishla(1))
guruh.create_task(ishla(2))
async with asyncio.timeout(5):
await sekin()
natija = await asyncio.to_thread(bloklovchi, arg)
asyncio.run(main())Qoidalar
async def → korutina obyekti; await yoki create_task kerak
TaskGroup — sukut tanlov, gather — oddiy holatlar uchun
CancelledError — BaseException, uni yutmang
loopda bloklovchi chaqiruv yo'q
to_thread — I/O uchun, ProcessPool — hisob uchun4. Batafsil misollar
Misol 1 — Hodisa sikli ichidan
"""Korutina obyekti va Task; loopga kirish; call_soon navbati; sleep(0) bilan navbat almashish; eager task."""
import asyncio
import inspect
async def salom() -> int:
return 42
async def loop_haqida() -> None:
loop = asyncio.get_running_loop()
print(f" AbstractEventLoop merosxo'ri: {isinstance(loop, asyncio.AbstractEventLoop)}")
print(f" ishlayapti: {loop.is_running()}, debug rejimi: {loop.get_debug()}")
async def vazifa_haqida() -> None:
vazifa = asyncio.create_task(salom(), name="salom-vazifa")
print(f" turi: {type(vazifa).__name__}, Future merosxo'ri: {isinstance(vazifa, asyncio.Future)}")
print(f" yaratilgan zahoti done(): {vazifa.done()} ← hali boshlanmagan")
natija = await vazifa
print(f" await dan keyin: natija={natija}, done={vazifa.done()}, nom={vazifa.get_name()}")
async def navbat_tartibi() -> None:
jurnal: list[str] = []
loop = asyncio.get_running_loop()
loop.call_soon(jurnal.append, "call_soon-1")
loop.call_soon(jurnal.append, "call_soon-2")
jurnal.append("korutina-davom-etdi")
await asyncio.sleep(0)
print(f" {jurnal}")
print(" ⭐ korutina await gacha ishlaydi, keyin navbatdagilar")
async def navbat_almashish() -> None:
jurnal: list[str] = []
async def hisobla(nom: str) -> None:
for i in range(3):
jurnal.append(f"{nom}{i}")
await asyncio.sleep(0) # ⭐ navbatni boshqaga berish
await asyncio.gather(hisobla("a"), hisobla("b"))
print(f" {jurnal}")
print(" ⭐ sleep(0) har safar navbatni almashtiradi")
async def tezkor_vazifa() -> None:
loop = asyncio.get_running_loop()
loop.set_task_factory(asyncio.eager_task_factory)
jurnal: list[str] = []
async def darhol() -> int:
jurnal.append("boshlandi")
return 1
vazifa = asyncio.create_task(darhol())
print(f" eager_task_factory bilan yaratilishdayoq ishga tushdi: {jurnal == ['boshlandi']}")
print(f" darhol done(): {vazifa.done()}")
await vazifa
def main() -> None:
print("=== 1. Korutina obyekti ===")
korutina = salom()
print(f" salom() natijasi: {type(korutina).__name__}, korutinami: {inspect.iscoroutine(korutina)}")
print(" ⚠️ chaqirish bajarish emas — kod hali ishlamadi")
print(f" asyncio.run(korutina) → {asyncio.run(korutina)}")
print("\n=== 2. Loopga kirish ===")
asyncio.run(loop_haqida())
try:
asyncio.get_running_loop()
except RuntimeError as xato:
print(f" loopdan tashqarida: RuntimeError: {xato}")
print("\n=== 3. Task ===")
asyncio.run(vazifa_haqida())
print("\n=== 4. call_soon navbati ===")
asyncio.run(navbat_tartibi())
print("\n=== 5. sleep(0) va navbat ===")
asyncio.run(navbat_almashish())
print("\n=== 6. Eager task (3.12+) ===")
asyncio.run(tezkor_vazifa())
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Korutina obyekti ===
salom() natijasi: coroutine, korutinami: True
⚠️ chaqirish bajarish emas — kod hali ishlamadi
asyncio.run(korutina) → 42
=== 2. Loopga kirish ===
AbstractEventLoop merosxo'ri: True
ishlayapti: True, debug rejimi: False
loopdan tashqarida: RuntimeError: no running event loop
=== 3. Task ===
turi: Task, Future merosxo'ri: True
yaratilgan zahoti done(): False ← hali boshlanmagan
await dan keyin: natija=42, done=True, nom=salom-vazifa
=== 4. call_soon navbati ===
['korutina-davom-etdi', 'call_soon-1', 'call_soon-2']
⭐ korutina await gacha ishlaydi, keyin navbatdagilar
=== 5. sleep(0) va navbat ===
['a0', 'b0', 'a1', 'b1', 'a2', 'b2']
⭐ sleep(0) har safar navbatni almashtiradi
=== 6. Eager task (3.12+) ===
eager_task_factory bilan yaratilishdayoq ishga tushdi: True
darhol done(): TrueNima ko'rsatdi: 2.1, 2.2, 2.3, 2.7-bo'limlar.
Misol 2 — gather, TaskGroup, bekor qilish
"""gather tartibi; return_exceptions; TaskGroup va ExceptionGroup; as_completed; timeout; cancel va finally."""
import asyncio
JURNAL: list[str] = []
async def kut(sekund: float, nom: str) -> str:
await asyncio.sleep(sekund)
JURNAL.append(nom)
return nom
async def yiqiladi(nom: str) -> str:
await asyncio.sleep(0.01)
raise ValueError(f"{nom} yiqildi")
async def gather_tartibi() -> None:
JURNAL.clear()
natijalar = await asyncio.gather(kut(0.03, "a"), kut(0.01, "b"), kut(0.02, "c"))
print(f" natijalar (kirish tartibida): {natijalar}")
print(f" tugash tartibi: {JURNAL}")
print(" ⭐ gather tartibni saqlaydi, garchi 'b' birinchi tugagan bo'lsa ham")
async def gather_xatolar() -> None:
natijalar = await asyncio.gather(kut(0.01, "yaxshi"), yiqiladi("yomon"), return_exceptions=True)
turlar = [type(n).__name__ if isinstance(n, BaseException) else n for n in natijalar]
print(f" return_exceptions=True → {turlar}")
try:
await asyncio.gather(kut(0.01, "yaxshi"), yiqiladi("yomon"))
except ValueError as xato:
print(f" return_exceptions=False → ValueError: {xato}")
async def guruh() -> None:
JURNAL.clear()
try:
async with asyncio.TaskGroup() as grp:
grp.create_task(kut(0.5, "uzoq"))
grp.create_task(yiqiladi("tez"))
except* ValueError as guruh_xatosi:
ichidagilar = [type(x).__name__ for x in guruh_xatosi.exceptions]
print(f" ushlandi: {type(guruh_xatosi).__name__} → {ichidagilar}")
print(f" ⭐ 'uzoq' vazifa bekor qilindi, jurnalga tushmadi: {JURNAL == []}")
async def tugash_tartibida() -> None:
kelajaklar = [kut(0.03, "a"), kut(0.01, "b"), kut(0.02, "c")]
tartib = [await k for k in asyncio.as_completed(kelajaklar)]
print(f" as_completed tartibi: {tartib} ← eng tez tugagani birinchi")
async def muddatlar() -> None:
try:
async with asyncio.timeout(0.02):
await kut(1.0, "juda-uzoq")
except TimeoutError:
print(" asyncio.timeout(0.02) → TimeoutError")
try:
await asyncio.wait_for(kut(1.0, "juda-uzoq"), timeout=0.02)
except TimeoutError:
print(" wait_for(timeout=0.02) → TimeoutError")
print(" ⭐ 14.5-darsdagidan farqli: vazifa haqiqatan bekor qilinadi")
async def bekor_qilish() -> None:
tozalandi: list[str] = []
async def tozalovchi() -> None:
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
tozalandi.append("CancelledError ushlandi")
raise # ⭐ qayta ko'tarish shart
finally:
tozalandi.append("finally bajarildi")
vazifa = asyncio.create_task(tozalovchi())
await asyncio.sleep(0)
vazifa.cancel()
try:
await vazifa
except asyncio.CancelledError:
print(" await vazifa → CancelledError")
print(f" vazifa.cancelled(): {vazifa.cancelled()}")
print(f" tozalash bosqichlari: {tozalandi}")
print(f" ⚠️ CancelledError Exception emas: {not issubclass(asyncio.CancelledError, Exception)}")
async def main() -> None:
print("=== 1. gather tartibi ===")
await gather_tartibi()
print("\n=== 2. gather va xatolar ===")
await gather_xatolar()
print("\n=== 3. TaskGroup ===")
await guruh()
print("\n=== 4. as_completed ===")
await tugash_tartibida()
print("\n=== 5. Muddatlar ===")
await muddatlar()
print("\n=== 6. Bekor qilish va tozalash ===")
await bekor_qilish()
if __name__ == "__main__":
asyncio.run(main())Natijaning muhim qismi:
=== 1. gather tartibi ===
natijalar (kirish tartibida): ['a', 'b', 'c']
tugash tartibi: ['b', 'c', 'a']
⭐ gather tartibni saqlaydi, garchi 'b' birinchi tugagan bo'lsa ham
=== 2. gather va xatolar ===
return_exceptions=True → ['yaxshi', 'ValueError']
return_exceptions=False → ValueError: yomon yiqildi
=== 3. TaskGroup ===
ushlandi: ExceptionGroup → ['ValueError']
⭐ 'uzoq' vazifa bekor qilindi, jurnalga tushmadi: True
=== 4. as_completed ===
as_completed tartibi: ['b', 'c', 'a'] ← eng tez tugagani birinchi
=== 5. Muddatlar ===
asyncio.timeout(0.02) → TimeoutError
wait_for(timeout=0.02) → TimeoutError
⭐ 14.5-darsdagidan farqli: vazifa haqiqatan bekor qilinadi
=== 6. Bekor qilish va tozalash ===
await vazifa → CancelledError
vazifa.cancelled(): True
tozalash bosqichlari: ['CancelledError ushlandi', 'finally bajarildi']
⚠️ CancelledError Exception emas: TrueNima ko'rsatdi: 2.4, 2.5-bo'limlar.
Misol 3 — Loopni bloklash va yechimlari
"""Bloklovchi chaqiruv butun loopni to'xtatadi; to_thread va run_in_executor bilan yechim; hisob uchun jarayonlar puli."""
import asyncio
import concurrent.futures as cf
import time
def bloklovchi_ish(sekund: float) -> str:
time.sleep(sekund) # ⚠️ asinxron emas
return f"{sekund} s bloklovchi ish tugadi"
def ogir_hisob(n: int) -> int:
return sum(i * i for i in range(n))
async def yengil_vazifa(nom: str, jurnal: list[str]) -> None:
await asyncio.sleep(0.05)
jurnal.append(nom)
async def blokli_variant() -> float:
jurnal: list[str] = []
bosh = time.perf_counter()
vazifa = asyncio.create_task(yengil_vazifa("yengil", jurnal))
bloklovchi_ish(0.4) # ⚠️ loop butunlay to'xtaydi
await vazifa
oraliq = time.perf_counter() - bosh
print(f" yengil vazifa 0.05 s lik edi, lekin jami: 0.4 s dan ko'p: {oraliq > 0.4}")
print(f" jurnal: {jurnal}")
return oraliq
async def toza_variant() -> float:
jurnal: list[str] = []
bosh = time.perf_counter()
vazifa = asyncio.create_task(yengil_vazifa("yengil", jurnal))
xabar = await asyncio.to_thread(bloklovchi_ish, 0.4)
await vazifa
oraliq = time.perf_counter() - bosh
print(f" to_thread natijasi: {xabar}")
print(f" yengil vazifa kutmadi, jami 0.4 s atrofida: {oraliq < 0.55}")
print(f" jurnal: {jurnal}")
return oraliq
async def parallel_bloklovchi() -> None:
bosh = time.perf_counter()
natijalar = await asyncio.gather(*(asyncio.to_thread(bloklovchi_ish, 0.2) for _ in range(5)))
oraliq = time.perf_counter() - bosh
print(f" 5 ta 0.2 s lik bloklovchi ish: {len(natijalar)} natija")
print(f" ketma-ket 1.0 s bo'lardi, parallel 0.5 s dan kam: {oraliq < 0.5}")
async def hisob_uchun_jarayonlar() -> None:
loop = asyncio.get_running_loop()
with cf.ProcessPoolExecutor(max_workers=4) as pul:
natijalar = await asyncio.gather(
*(loop.run_in_executor(pul, ogir_hisob, 2_000_000) for _ in range(4))
)
kutilgan = sum(i * i for i in range(2_000_000))
print(f" 4 ta og'ir hisob jarayonlar pulida bajarildi: {len(natijalar) == 4}")
print(f" natijalar to'g'ri: {all(n == kutilgan for n in natijalar)}")
print(" ⭐ sof Python hisob — iplar emas, jarayonlar (14.5-dars)")
async def main() -> None:
print("=== 1. ⚠️ Bloklovchi chaqiruv ===")
blokli = await blokli_variant()
print("\n=== 2. ✅ to_thread bilan ===")
toza = await toza_variant()
print("\n=== 3. Ikkisining farqi ===")
print(f" bloklovchi variant sekinroq: {blokli > toza}")
print(" ⭐ ish hajmi bir xil — farq faqat loop bo'shligida")
print("\n=== 4. Ko'p bloklovchi ishni birga bajarish ===")
await parallel_bloklovchi()
print("\n=== 5. Og'ir hisob uchun jarayonlar puli ===")
await hisob_uchun_jarayonlar()
if __name__ == "__main__":
asyncio.run(main())Natijaning muhim qismi:
=== 1. ⚠️ Bloklovchi chaqiruv ===
yengil vazifa 0.05 s lik edi, lekin jami: 0.4 s dan ko'p: True
jurnal: ['yengil']
=== 2. ✅ to_thread bilan ===
to_thread natijasi: 0.4 s bloklovchi ish tugadi
yengil vazifa kutmadi, jami 0.4 s atrofida: True
jurnal: ['yengil']
=== 3. Ikkisining farqi ===
bloklovchi variant sekinroq: True
⭐ ish hajmi bir xil — farq faqat loop bo'shligida
=== 4. Ko'p bloklovchi ishni birga bajarish ===
5 ta 0.2 s lik bloklovchi ish: 5 natija
ketma-ket 1.0 s bo'lardi, parallel 0.5 s dan kam: True
=== 5. Og'ir hisob uchun jarayonlar puli ===
4 ta og'ir hisob jarayonlar pulida bajarildi: True
natijalar to'g'ri: True
⭐ sof Python hisob — iplar emas, jarayonlar (14.5-dars)Nima ko'rsatdi: 2.6-bo'lim.
Misol 4 — Amaliy: 500 ta ulanishli xizmat
Kirishdagi vaziyat: minglab ulanishni bitta jarayonda ushlab turish. Bu yerda tarmoqni taqlid qilamiz (asyncio.sleep — haqiqiy kutish bilan bir xil xulq), lekin qolgan hamma narsa haqiqiy: bir vaqtdagi so'rovlar chegarasi (semafor), muddat, qayta urinish, xatolarni yig'ish va xushmuomala to'xtatish.
"""Semafor bilan chegaralash; muddat va qayta urinish; TaskGroup bilan yig'ish; ketma-ket variant bilan solishtirish."""
import asyncio
import threading
import time
from dataclasses import dataclass, field
SOROVLAR = [f"/api/hujjat/{i}" for i in range(500)]
BIR_VAQTDA = 50
ASOSIY_IPLAR = threading.active_count() # dastur boshidagi iplar soni
@dataclass(slots=True)
class Hisobot:
muvaffaqiyat: list[str] = field(default_factory=list)
xatolar: list[tuple[str, str]] = field(default_factory=list)
urinishlar: int = 0
faol: int = 0
eng_kop_faol: int = 0
async def soro(yol: str) -> str:
"""Tarmoq so'rovini taqlid qiladi: kutish + aniqlangan nosozliklar."""
raqam = int(yol.rsplit("/", 1)[1])
await asyncio.sleep(0.002 + (raqam % 5) * 0.001)
if raqam % 100 == 7:
raise ConnectionError("ulanish uzildi")
if raqam % 100 == 13:
await asyncio.sleep(1.0) # muddatdan oshadi
return f"javob:{raqam}"
async def ishonchli_soro(yol: str, semafor: asyncio.Semaphore, hisobot: Hisobot) -> None:
async with semafor: # ⭐ bir vaqtda ko'pi bilan BIR_VAQTDA ta
hisobot.faol += 1
hisobot.eng_kop_faol = max(hisobot.eng_kop_faol, hisobot.faol)
try:
for urinish in range(1, 3):
hisobot.urinishlar += 1
try:
async with asyncio.timeout(0.5):
hisobot.muvaffaqiyat.append(await soro(yol))
return
except TimeoutError:
hisobot.xatolar.append((yol, "muddat tugadi"))
return # takrorlansa yana osiladi
except ConnectionError as xato:
if urinish == 2:
hisobot.xatolar.append((yol, str(xato)))
finally:
hisobot.faol -= 1
async def hammasini_bajar() -> tuple[Hisobot, float]:
hisobot = Hisobot()
semafor = asyncio.Semaphore(BIR_VAQTDA)
bosh = time.perf_counter()
async with asyncio.TaskGroup() as guruh:
for yol in SOROVLAR:
guruh.create_task(ishonchli_soro(yol, semafor, hisobot))
return hisobot, time.perf_counter() - bosh
async def ketma_ket(nechta: int) -> float:
bosh = time.perf_counter()
for yol in SOROVLAR[:nechta]:
try:
async with asyncio.timeout(0.5):
await soro(yol)
except (TimeoutError, ConnectionError):
pass
return time.perf_counter() - bosh
async def main() -> None:
print("=== 1. 500 so'rov, bitta ip ===")
hisobot, vaqt = await hammasini_bajar()
jami = len(hisobot.muvaffaqiyat) + len(hisobot.xatolar)
print(f" muvaffaqiyatli: {len(hisobot.muvaffaqiyat)}, xato: {len(hisobot.xatolar)}")
print(f" hammasi hisobga olindi: {jami == len(SOROVLAR)}")
print(f" qayta urinishlar bo'ldi: {hisobot.urinishlar > len(SOROVLAR)}")
print("\n=== 2. Xatolar turi bo'yicha ===")
turlar: dict[str, int] = {}
for _, sabab in hisobot.xatolar:
turlar[sabab] = turlar.get(sabab, 0) + 1
for sabab, son in sorted(turlar.items()):
print(f" {sabab}: {son}")
print("\n=== 3. Semafor chegarasi ===")
print(f" eng ko'p bir vaqtda faol so'rov: {hisobot.eng_kop_faol}")
print(f" chegaradan oshmadi: {hisobot.eng_kop_faol <= BIR_VAQTDA}")
print(f" bitta ipda bajarildi: {threading.active_count() == ASOSIY_IPLAR}")
print("\n=== 4. Ketma-ket variant bilan solishtirish ===")
ketma_ket_vaqti = await ketma_ket(100)
taxminiy_500 = ketma_ket_vaqti * 5 # 100 so'rovdan 500 ga chiziqli o'sish
print(f" asinxron variant ketma-ketdan tez: {vaqt < taxminiy_500}")
print(f" farq kamida 3 barobar: {taxminiy_500 / vaqt >= 3}")
print("\n=== 5. Qoidalar ===")
print(" ✅ Semaphore — bir vaqtdagi so'rovlar soni cheklangan")
print(" ✅ asyncio.timeout — osilib qolgan so'rov bekor qilinadi")
print(" ✅ TaskGroup — hamma vazifa tugashi kafolatlangan")
print(" ✅ hisobot — har so'rov natijasi qayd etiladi")
if __name__ == "__main__":
asyncio.run(main())Natijaning muhim qismi:
=== 1. 500 so'rov, bitta ip ===
muvaffaqiyatli: 490, xato: 10
hammasi hisobga olindi: True
qayta urinishlar bo'ldi: True
=== 2. Xatolar turi bo'yicha ===
muddat tugadi: 5
ulanish uzildi: 5
=== 3. Semafor chegarasi ===
eng ko'p bir vaqtda faol so'rov: 50
chegaradan oshmadi: True
bitta ipda bajarildi: True
=== 4. Ketma-ket variant bilan solishtirish ===
asinxron variant ketma-ketdan tez: True
farq kamida 3 barobar: True
=== 5. Qoidalar ===
✅ Semaphore — bir vaqtdagi so'rovlar soni cheklangan
✅ asyncio.timeout — osilib qolgan so'rov bekor qilinadi
✅ TaskGroup — hamma vazifa tugashi kafolatlangan
✅ hisobot — har so'rov natijasi qayd etiladiNima ko'rsatdi: 2.4, 2.5-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"asyncio — parallel bajarish" |
Bitta ipda konkurent bajarish; parallel emas |
"async def chaqirilsa ishga tushadi" |
Faqat korutina obyekti hosil bo'ladi |
"asyncio har doim tezroq" |
Faqat I/O kutish ko'p bo'lganda |
"await kutishni bildiradi" |
Nazoratni loopga qaytarish nuqtasi |
| "Iplar kerak emas" | Bloklovchi kutubxonalar uchun to_thread kerak |
"CancelledError ni except Exception ushlaydi" |
Yo'q — u BaseException |
"gather natijalarni tugash tartibida beradi" |
Kirish tartibida |
"timeout vazifani to'xtatmaydi" |
asyncio da to'xtatadi (concurrent.futures dan farqli) |
6. Keng tarqalgan xatolar va yechimlari
1. Loopda bloklovchi chaqiruv
time.sleep(1); requests.get(url) # ❌ hamma vazifa to'xtaydi
await asyncio.sleep(1) # ✅
await asyncio.to_thread(requests.get, url)2. await unutilgan
asyncio.sleep(1) # ❌ RuntimeWarning: never awaited
await asyncio.sleep(1) # ✅3. Vazifaga havola saqlanmagan
asyncio.create_task(fon_ishi()) # ⚠️ yig'ib tashlanishi mumkin
VAZIFALAR.add(t := asyncio.create_task(fon_ishi()))
t.add_done_callback(VAZIFALAR.discard) # ✅4. CancelledError ni yutish
except asyncio.CancelledError:
pass # ❌ bekor qilish ishlamay qoladi
raise # ✅5. Har so'rov uchun yangi sessiya
async with httpx.AsyncClient() as m: # ❌ har so'rovda
SESSIYA = httpx.AsyncClient() # ✅ bir marta6. Chegarasiz vazifa yaratish
await asyncio.gather(*(soro(u) for u in 100_000 * [...])) # ❌
async with asyncio.Semaphore(50): # ✅ chegara7. asyncio.run ni tsikl ichida chaqirish
for x in malumot:
asyncio.run(ishla(x)) # ❌ har safar yangi loop
asyncio.run(main()) # ✅ bitta kirish nuqtasi8. Sinxron kod ichidan await
def oddiy():
natija = await ishla() # ❌ SyntaxError
async def oddiy(): ... # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 14.1-dars (o'tilgan): korutina — eng arzon konkurentlik birligi
- 14.5-dars (o'tilgan):
run_in_executororqali pullar bilan bog'lash - 14.7-dars: amaliy asinxron kod — mijoz, navbat, asinxron iterator
- 14.8-dars: qaysi modelni qachon tanlash
- 9.4-dars (o'tilgan):
ExceptionGroupvaexcept* - 17-qism: asinxron kodni testlash
- 20-qism: FastAPI — asinxron veb-freymvork
- 29-qism: minglab ulanishli xizmatlarni miqyoslash
8. Eng yaxshi amaliyotlar
Kirish nuqtasi — bitta
asyncio.run(main()).TaskGroupni sukut tanlov qiling — u vazifalarni unutmaydi va xatoda qolganlarini bekor qiladi.Loopni hech qachon bloklamang. Bloklovchi kutubxona bo'lsa —
to_thread.Har vazifaga muddat bering (
asyncio.timeout).Bir vaqtdagi ishlar sonini
Semaphorebilan cheklang.CancelledErrorni qayta ko'taring, tozalashnifinallyda qiling.Fon vazifalariga havola saqlang.
Ishlab chiqishda
debug=True— sekin callback larni ko'rsatadi.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
import asyncio, inspect
1. async def f(): return 1
print(type(f()).__name__)
2. async def f(): return 1
print(inspect.iscoroutinefunction(f))
3. async def f(): return 1
print(asyncio.run(f()))
4. async def m():
return type(asyncio.create_task(asyncio.sleep(0))).__name__
print(asyncio.run(m()))
5. async def m():
t = asyncio.create_task(asyncio.sleep(0))
d = t.done()
await t
return d, t.done()
print(asyncio.run(m()))
6. async def k(n): return n
async def m(): return await asyncio.gather(k(3), k(1), k(2))
print(asyncio.run(m()))
7. print(issubclass(asyncio.CancelledError, Exception))
8. print(issubclass(asyncio.CancelledError, BaseException))
9. async def m():
try:
await asyncio.wait_for(asyncio.sleep(1), timeout=0.01)
except TimeoutError:
return "muddat"
print(asyncio.run(m()))
10. async def m():
t = asyncio.create_task(asyncio.sleep(1))
await asyncio.sleep(0)
t.cancel()
try:
await t
except asyncio.CancelledError:
return t.cancelled()
print(asyncio.run(m()))
11. async def m(): return isinstance(asyncio.create_task(asyncio.sleep(0)), asyncio.Future)
print(asyncio.run(m()))
12. try:
asyncio.get_running_loop()
except RuntimeError as e:
print(type(e).__name__)Javoblar
coroutineTrue1Task(False, True)[3, 1, 2]— kirish tartibidaFalse— uBaseExceptionTruemuddatTrueTrue—TaskFuturemerosxo'riRuntimeError
Vazifa 2: Xatolarni tuzating
1. async def yukla(url):
return requests.get(url).text
2. async def main():
asyncio.sleep(1)
print("tugadi")
3. async def main():
for x in malumot:
await ishla(x) # 10 000 element, har biri I/O
4. async def ishchi():
try:
await uzoq_ish()
except Exception:
qayd("xato")
5. for element in malumot:
asyncio.run(ishla(element))Javoblar
1. async def yukla(mijoz, url): # httpx.AsyncClient
javob = await mijoz.get(url)
return javob.text
# yoki bloklovchi kutubxona majbur bo'lsa:
# return (await asyncio.to_thread(requests.get, url)).text
2. async def main():
await asyncio.sleep(1) # await qo'shildi
print("tugadi")
3. semafor = asyncio.Semaphore(50)
async def chegarali(x):
async with semafor:
return await ishla(x)
async with asyncio.TaskGroup() as g:
for x in malumot:
g.create_task(chegarali(x))
4. async def ishchi():
try:
await uzoq_ish()
except asyncio.CancelledError:
raise # bekor qilishni yutmaymiz
except Exception:
qayd("xato")
5. async def main():
async with asyncio.TaskGroup() as g:
for element in malumot:
g.create_task(ishla(element))
asyncio.run(main()) # bitta loopVazifa 3: Mini hodisa sikli
asyncio siz, generatorlar asosida ishlaydigan soddalashtirilgan hodisa sikli yozing:
yieldni "nazoratni qaytarish" nuqtasi sifatida ishlatingsleep(t)ni taqlid qiluvchi vosita qo'shing (time.monotonicbilan)dequeda tayyor vazifalar navbatini yuriting- Uchta "korutina" ni birga bajarib, navbat almashishini ko'rsating
asynciobilan bir xil natija berishini tekshiring
Vazifa 4: Bloklash detektori
- Loopni kuzatuvchi vazifa yozing: har 10 ms da
asyncio.sleep(0.01)qilib, haqiqiy o'tgan vaqtni o'lchasin - Kechikish 50 ms dan oshsa — ogohlantirish chiqarsin
- Ataylab bloklovchi kod qo'shib, detektorni sinab ko'ring
asyncio.run(main(), debug=True)ogohlantirishlari bilan solishtiring
Vazifa 5: Ishonchli asinxron mijoz
AsinxronMijoz yozing:
- Semafor bilan chegara, har so'rovga muddat
- Eksponensial kechikish bilan qayta urinish (
0.1 · 2^n) - Ketma-ket 5 xatodan keyin "zanjir uzgich" (circuit breaker) ochilsin
- Statistika: so'rovlar, qayta urinishlar, muvaffaqiyat foizi
- Xushmuomala to'xtatish:
cancelda faol so'rovlar tugashini kutsin (shield)
Vazifa 6: Asinxron quvur
- Uch bosqich: olish (I/O) → tahlil (hisob) → saqlash (I/O)
- Bosqichlarni
asyncio.Queuebilan ulang - Hisob bosqichini
run_in_executorga bering - Har bosqichda ishchilar sonini sozlanadigan qiling
- O'tkazuvchanlikni o'lchang va eng tor joyni toping
Vazifa 7: O'ylash
JavaScript'da hodisa sikli tilning o'zida — har bir funksiya asinxron muhitda ishlaydi. Go'da goroutine'lar runtime tomonidan rejalashtiriladi va "bloklovchi" chaqiruv avtomatik ravishda boshqa goroutine'ga yo'l beradi. Python'da esa async funksiyalar alohida "rang"ga ega: sinxron funksiyadan await qilib bo'lmaydi. Nega Python shu yo'lni tanlagan va bu qanday muammolarni keltirib chiqaradi?
Javob
Qisqa javob: Python asyncio ni kutubxona sifatida, mavjud tilga zarar bermay qo'shdi. Shuning uchun asinxronlik async/await kalit so'zlari bilan ko'rinadigan qilindi — bu "funksiya rangi" (function colouring) muammosini keltirib chiqaradi, lekin ayni paytda kod qayerda to'xtashini aniq ko'rsatadi.
1. Uch xil yondashuv
| Til | Model | Rang muammosi |
|---|---|---|
| JavaScript | Tug'ma hodisa sikli, Promise |
Bor, lekin butun ekotizim asinxron |
| Go | Runtime M:N rejalashtiruvchi | Yo'q — har chaqiruv "bloklashi" mumkin |
| Java 21+ | Virtual iplar | Yo'q — eski kod o'zgarmay ishlaydi |
| Python | Kutubxona + til kalit so'zlari | Bor va sezilarli |
2. "Funksiya rangi" muammosi
asyncfunksiyani faqatasyncfunksiyadanawaitqilish mumkin- Sinxron kutubxona (masalan,
requests,psycopg2) asinxron kodda ishlatilmaydi - Natijada ekotizim ikkiga bo'linadi:
requests/httpx,psycopg2/asyncpg,redis/redis.asyncio - Kutubxona mualliflari ko'pincha ikkita variant yozishga majbur
3. Nega Python shunday qilgan
- Orqaga moslik: mavjud millionlab qator sinxron kod buzilmasligi kerak edi
- GIL va C kengaytmalari: Go'dagi kabi avtomatik rejalashtirish C stekini boshqarishni talab qiladi
- Aniqlik:
awaitkod qayerda to'xtashini ko'rsatadi — bu poyga holatlarini kamaytiradi:awaitorasidagi kod atomar - Kutubxona sifatida rivojlanish:
asynciotildan alohida yangilanishi mumkin
4. Aniqlikning foydasi
# Go/Java: har qatorda boshqa goroutine ishga tushishi mumkin
# Python: faqat await da
hisob = mijoz.balans # bu yerda hech kim aralasha olmaydi
hisob -= summa # atomar
await saqla(hisob) # ⭐ faqat shu yerda navbat almashadiBu threading dagi ko'p poyga holatini asyncio'da yo'q qiladi (14.3-dars).
5. Kelajak
Python ham virtual iplarga o'xshash yechimlarni muhokama qilmoqda (greenlet, gevent allaqachon mavjud), lekin ular C kengaytmalari bilan mos kelmasligi mumkin. 3.13+ dagi erkin ipli (free-threaded) rejim boshqa yo'l: haqiqiy parallel iplar (12.6-dars).
6. Xulosa
- Python asinxronlikni kutubxona sifatida qo'shgani uchun
async"rang" paydo bo'ldi - Rang — bo'linish narxi, lekin
awaitaniqligini beradi - Aniqlik poyga holatlarini kamaytiradi — bu katta yutuq
- Go/Java yo'li qulayroq, lekin Python'ning C ekotizimi uchun mos emas
Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.
Xulosa
Bu darsda hodisa siklining ichki mexanizmini ko'rdik.
Eng muhim uch fikr:
Hodisa sikli — bitta ipda navbat bilan ishlaydigan rejalashtiruvchi.
async defchaqirilganda kod bajarilmaydi — korutina obyekti hosil bo'ladi; uniawaityokicreate_taskishga tushiradi.awaitesa "kutish" emas, nazoratni loopga qaytarish nuqtasi: shu payt boshqa vazifalar ishlaydi. Shuning uchun minglab ulanish bitta ipda sig'adi — har biri ip emas, kichik obyekt.TaskGroupvatimeout— zamonaviy tanlov.gathernatijalarni kirish tartibida beradi,TaskGroupesa bittasi yiqilsa qolganlarini bekor qilib, xatolarniExceptionGroupga yig'adi.asyncio.timeoutvazifani haqiqatan bekor qiladi — buconcurrent.futuresdagi muddatdan muhim farq.CancelledError—BaseException, uniexcept Exceptionushlamaydi va uni yutib yubormaslik kerak.Loopni bloklash — eng qimmat xato. Bitta
time.sleepyokirequests.getbutun serverni to'xtatadi: hamma vazifa kutadi. Yechim — asinxron kutubxona, bo'lmasaasyncio.to_thread(I/O uchun) yokirun_in_executor+ jarayonlar puli (hisob uchun). Ishlab chiqishdadebug=Trueshunday joylarni topib beradi.
Keyingi darsda bu mexanizmni amaliyotga ko'chiramiz: asinxron mijoz, asyncio.Queue bilan quvur, asinxron iterator va kontekst menejerlar.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!