Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Uch model
- 2.2. Konkurentlik va parallellik
- 2.3. Rejalashtirish: majburiy va hamkorlikdagi
- 2.4. Izolyatsiya va xavfsizlik
- 2.5. Identifikatorlar
- 2.6. Miqyos: nechtasi mumkin
- 2.7. Qaysi birini qachon (qisqacha)
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Uch model, bitta vazifa
- Misol 2 — Narx va miqyos
- Misol 3 — Rejalashtirish: majburiy va hamkorlikdagi
- Misol 4 — Amaliy: ish turiga qarab model tanlash
- 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.1-dars: Jarayon, oqim, korutina farqi
14-QISM — KONKURENTLIK VA PARALLELLIK · 1-dars
1. Kirish va motivatsiya
12.5–12.6-darslarda GIL ni o'rgandik va "iplar, jarayonlar, asyncio" degan so'zlar bir necha marta uchradi. Endi ularni tizimli ko'rib chiqamiz — chunki noto'g'ri tanlov qimmatga tushadi.
Real vaziyat. Xabarnoma servisi 5 000 ta qurilmaga push-xabar yuborishi kerak edi. Har yuborish — 200 ms lik tarmoq so'rovi. Birinchi versiya ketma-ket ishladi: 5 000 × 0.2 s ≈ 17 daqiqa. Jamoa har xabar uchun bitta ip yaratdi — 5 000 ip. Server 1.2 GB xotira yedi, kontekst almashinuvi tufayli sekinlashdi va ba'zi iplar "ochlik"da qoldi.
To'g'ri yechim — korutinalar: 5 000 ta korutina ~4 MB xotira oladi va bitta ipda ishlaydi; yuborish 25 soniyada tugadi.
Uch model — uch xil narx va uch xil kafolat. Ularni farqlamasdan "konkurentlik" haqida gapirib bo'lmaydi.
Bu darsda:
- Jarayon, ip (thread) va korutina: nima farqi bor
- Yaratish narxi, xotira va miqyos: o'lchovlar bilan
- Rejalashtirish: majburiy (preemptive) va hamkorlikdagi (cooperative)
- Izolyatsiya va ma'lumot almashish
- Konkurentlik va parallellik farqi
- Har modelning identifikatorlari: PID, ip ID, vazifa nomi
- Amaliy: ish turiga qarab model tanlash
2. Nazariya — chuqur tushuntirish
2.1. Uch model
| Jarayon | Ip (thread) | Korutina | |
|---|---|---|---|
| Kim boshqaradi | Operatsion tizim | Operatsion tizim | Python (hodisa sikli) |
| Xotira | O'z manzil fazosi | Umumiy (jarayon ichida) | Umumiy (ip ichida) |
| Yaratish narxi | Eng qimmat (ms) | O'rtacha (~0.1 ms) | Eng arzon (~µs) |
| Bittasiga xotira | MB lar | ~50 KB – 8 MB (stek) | ~1 KB |
| Almashinuv | Qimmat (MMU, kesh) | O'rtacha (yadro) | Juda arzon (funksiya chaqiruvi kabi) |
| Parallellik (CPU) | To'liq | GIL bilan yo'q | Yo'q |
| Nosozlik izolyatsiyasi | Yiqilsa boshqalar ishlaydi | Butun jarayon yiqiladi | Butun jarayon |
| Ma'lumot almashish | pickle, navbat, umumiy xotira |
To'g'ridan-to'g'ri (qulf kerak) | To'g'ridan-to'g'ri (qulf odatda kerak emas) |
| Python'da | multiprocessing |
threading |
asyncio |
2.2. Konkurentlik va parallellik
| Ma'nosi | Misol | |
|---|---|---|
| Konkurentlik | Ko'p ishni navbat bilan olib borish | Bitta oshpaz uch taomni almashib pishiradi |
| Parallellik | Ko'p ishni bir vaqtda bajarish | Uch oshpaz uch taomni birga pishiradi |
asyncio— konkurentlik, parallellik emas (bitta ip)- Iplar — Python kodi uchun konkurentlik, I/O va GIL bo'shatilganda parallellik
- Jarayonlar — haqiqiy parallellik
Ko'pincha kerak bo'lgani — konkurentlik: kutish vaqtini foydali ishga aylantirish.
2.3. Rejalashtirish: majburiy va hamkorlikdagi
Iplar — majburiy (preemptive): OT istalgan paytda ipni to'xtatib, boshqasiga o'tadi (Python'da GIL almashinuvi bilan birga — 12.5-dars).
def ishchi():
while True:
hisobla() # ip o'zi hech narsa qilmasa ham almashadiKorutinalar — hamkorlikdagi (cooperative): boshqaruv faqat await da uzatiladi.
async def ishchi():
while True:
hisobla() # ⚠️ await yo'q — butun sikl to'xtaydi
await asyncio.sleep(0) # ✅ boshqalarga navbat berish| Iplar | Korutinalar | |
|---|---|---|
| Almashinuv nuqtalari | Oldindan bilib bo'lmaydi | Faqat await |
| Poyga holatlari | Ko'p (12.5-dars) | Kamroq, lekin bor |
| Bloklovchi kod | Boshqa iplarga xalaqit bermaydi | Butun siklni to'xtatadi |
| Debug | Qiyinroq | Osonroq (almashinuv ko'rinadi) |
2.4. Izolyatsiya va xavfsizlik
| Model | Yiqilish ta'siri | Umumiy holat |
|---|---|---|
| Jarayon | Faqat o'zi yiqiladi | Yo'q (aniq uzatish kerak) |
| Ip | Butun jarayon yiqilishi mumkin (C kengaytma xatosi) |
Bor — qulf kerak |
| Korutina | Istisno vazifada qoladi, lekin sikl davom etadi | Bor — lekin await orasida almashinuv yo'q |
Shuning uchun ishonchsiz kod yoki C kutubxonalari uchun jarayon xavfsizroq.
2.5. Identifikatorlar
os.getpid() # jarayon
threading.get_ident() # ip (OT ID si emas, Python ID si)
threading.current_thread().name # ip nomi
asyncio.current_task().get_name() # vazifa nomiLoglarda shu uchtasini chiqarish — konkurent tizimni tushunishning eng arzon usuli.
2.6. Miqyos: nechtasi mumkin
| Model | Amaliy chegara | Nima cheklaydi |
|---|---|---|
| Jarayon | Yadrolar soni × 1–2 | Xotira, kontekst almashinuvi |
| Ip | Bir necha yuz – bir necha ming | Stek xotirasi, yadro resurslari |
| Korutina | Yuz minglab | Faqat xotira (~1 KB) |
"Har ulanish uchun bitta ip" — 1990-yillar modeli; bugun 10 000 ulanish uchun korutinalar ishlatiladi.
2.7. Qaysi birini qachon (qisqacha)
| Ish | Model |
|---|---|
| Ko'p tarmoq so'rovi, ulanishlar minglab | asyncio |
I/O, lekin kutubxona bloklovchi (requests, DB drayveri) |
Iplar |
| Sof Python CPU hisob | Jarayonlar |
C kutubxona GIL ni bo'shatadi (NumPy, hashlib) |
Iplar |
| Ishonchsiz yoki yiqiluvchan kod | Jarayonlar |
Batafsil — 14.8-darsda.
3. Tez ma'lumotnoma
import threading, multiprocessing, asyncio
threading.Thread(target=f).start() # ip
multiprocessing.Process(target=f).start() # jarayon
asyncio.run(main()) # korutinalar sikli
os.getpid() threading.get_ident() asyncio.current_task().get_name()Taqqoslash
jarayon: izolyatsiya, parallellik, qimmat
ip: umumiy xotira, GIL, qulflar kerak
korutina: eng arzon, bitta ip, await da almashadi
konkurentlik ≠ parallellik4. Batafsil misollar
Misol 1 — Uch model, bitta vazifa
"""Bir xil 'so'rovlar' uch model bilan: ketma-ket, iplar, korutinalar, jarayonlar; identifikatorlar va vaqt."""
import asyncio
import multiprocessing as mp
import os
import threading
import time
SOROVLAR = 12
KUTISH = 0.1
def sorov(nomer: int) -> tuple[int, int, str]:
"""Bloklovchi 'tarmoq so'rovi'."""
time.sleep(KUTISH)
return nomer, os.getpid(), threading.current_thread().name
async def async_sorov(nomer: int) -> tuple[int, int, str]:
await asyncio.sleep(KUTISH)
vazifa = asyncio.current_task()
return nomer, os.getpid(), vazifa.get_name() if vazifa else "?"
def ketma_ket() -> list[tuple[int, int, str]]:
return [sorov(i) for i in range(SOROVLAR)]
def iplar_bilan() -> list[tuple[int, int, str]]:
natijalar: list[tuple[int, int, str]] = []
qulf = threading.Lock()
def ishchi(n: int) -> None:
natija = sorov(n)
with qulf:
natijalar.append(natija)
iplar = [threading.Thread(target=ishchi, args=(i,), name=f"ip-{i}") for i in range(SOROVLAR)]
for ip in iplar:
ip.start()
for ip in iplar:
ip.join()
return natijalar
def korutinalar_bilan() -> list[tuple[int, int, str]]:
async def hammasi() -> list[tuple[int, int, str]]:
vazifalar = [asyncio.create_task(async_sorov(i), name=f"vazifa-{i}") for i in range(SOROVLAR)]
return list(await asyncio.gather(*vazifalar))
return asyncio.run(hammasi())
def olch(funksiya) -> tuple[float, list[tuple[int, int, str]]]:
bosh = time.perf_counter()
natija = funksiya()
return time.perf_counter() - bosh, natija
if __name__ == "__main__":
print("=== 1. Asosiy jarayon ===")
print(f" PID: {os.getpid() > 0}, asosiy ip: {threading.current_thread().name}, yadrolar: {os.cpu_count() >= 1}")
print("\n=== 2. Uch model ===")
t_ket, n_ket = olch(ketma_ket)
t_ip, n_ip = olch(iplar_bilan)
t_as, n_as = olch(korutinalar_bilan)
print(f" ketma-ket {SOROVLAR} × {KUTISH} s kutish → kamida {SOROVLAR * KUTISH:.1f} s: {t_ket >= SOROVLAR * KUTISH * 0.95}")
print(f" iplar kamida 5 barobar tez: {t_ket / t_ip >= 5}")
print(f" korutinalar kamida 5 barobar tez: {t_ket / t_as >= 5}")
print(f" natijalar soni bir xil: {len(n_ket) == len(n_ip) == len(n_as) == SOROVLAR}")
print("\n=== 3. Kim qayerda ishladi ===")
print(f" ketma-ket: hammasi bitta ipda: {len({n for _, _, n in n_ket}) == 1}")
print(f" iplar: {len({n for _, _, n in n_ip})} xil ip nomi")
print(f" korutinalar: {len({n for _, _, n in n_as})} xil vazifa nomi, hammasi bitta jarayonda: "
f"{len({p for _, p, _ in n_as}) == 1}")
print(f" korutina nomlari: {sorted(n for _, _, n in n_as)[:3]} ...")
print("\n=== 4. Jarayonlar ===")
with mp.Pool(processes=4) as pul:
bosh = time.perf_counter()
n_jar = pul.map(sorov, range(SOROVLAR))
t_jar = time.perf_counter() - bosh
pidlar = {p for _, p, _ in n_jar}
print(f" {len(pidlar)} xil PID da bajarildi, asosiy jarayondan farq qiladi: {os.getpid() not in pidlar}")
print(f" ketma-ketdan tez: {t_ket / t_jar >= 2}")
print(" ⭐ I/O uchun jarayonlar ortiqcha: iplar va korutinalar arzonroq")Natijaning muhim qismi:
=== 1. Asosiy jarayon ===
PID: True, asosiy ip: MainThread, yadrolar: True
=== 2. Uch model ===
ketma-ket 12 × 0.1 s kutish → kamida 1.2 s: True
iplar kamida 5 barobar tez: True
korutinalar kamida 5 barobar tez: True
natijalar soni bir xil: True
=== 3. Kim qayerda ishladi ===
ketma-ket: hammasi bitta ipda: True
iplar: 12 xil ip nomi
korutinalar: 12 xil vazifa nomi, hammasi bitta jarayonda: True
korutina nomlari: ['vazifa-0', 'vazifa-1', 'vazifa-10'] ...
=== 4. Jarayonlar ===
4 xil PID da bajarildi, asosiy jarayondan farq qiladi: True
ketma-ketdan tez: True
⭐ I/O uchun jarayonlar ortiqcha: iplar va korutinalar arzonroqNima ko'rsatdi: 2.1, 2.2, 2.5-bo'limlar.
Misol 2 — Narx va miqyos
"""Yaratish vaqti; bitta birlikka xotira; 10 000 korutina va 10 000 ip farqi; amaliy chegaralar."""
import asyncio
import gc
import multiprocessing as mp
import sys
import threading
import time
import tracemalloc
def hech_narsa() -> None:
pass
async def async_hech_narsa() -> None:
await asyncio.sleep(0)
def iplar_yarat(n: int) -> None:
iplar = [threading.Thread(target=hech_narsa) for _ in range(n)]
for ip in iplar:
ip.start()
for ip in iplar:
ip.join()
def korutinalar_yarat(n: int) -> None:
async def hammasi() -> None:
await asyncio.gather(*(async_hech_narsa() for _ in range(n)))
asyncio.run(hammasi())
def jarayonlar_yarat(n: int) -> None:
jarayonlar = [mp.Process(target=hech_narsa) for _ in range(n)]
for j in jarayonlar:
j.start()
for j in jarayonlar:
j.join()
def olch(funksiya, n: int, takror: int = 3) -> float:
eng = float("inf")
for _ in range(takror):
bosh = time.perf_counter()
funksiya(n)
eng = min(eng, time.perf_counter() - bosh)
return eng
if __name__ == "__main__":
print("=== 1. 100 birlik yaratish va tugatish ===")
t_ip = olch(iplar_yarat, 100)
t_kor = olch(korutinalar_yarat, 100)
t_jar = olch(jarayonlar_yarat, 8, takror=1)
print(f" korutinalar iplardan tez: {t_kor < t_ip}")
print(f" 8 jarayon 100 ipdan ham uzoq vaqt oldi: {t_jar > t_ip}")
print(f" jarayon yaratish korutinadan kamida 20 barobar qimmat (bittasiga): "
f"{(t_jar / 8) / (t_kor / 100) >= 20}")
print("\n=== 2. Miqyos: 10 000 birlik ===")
t_10k = olch(korutinalar_yarat, 10_000, takror=1)
print(f" 10 000 korutina yaratildi va tugadi: {t_10k < 5.0}")
print(f" bir korutinaga o'rtacha vaqt 100 korutinadagidan yomonlashmadi: "
f"{t_10k / 10_000 <= (t_kor / 100) * 3}")
print(" ⚠️ 10 000 ip yaratish ko'p tizimlarda xotira va yadro chegarasiga uriladi")
print("\n=== 3. Vazifaga qancha xotira ===")
async def kop_vazifa(n: int) -> int:
vazifalar = [asyncio.create_task(asyncio.sleep(0.05)) for _ in range(n)]
joriy, _ = tracemalloc.get_traced_memory()
await asyncio.gather(*vazifalar)
return joriy
gc.collect()
tracemalloc.start()
xotira = asyncio.run(kop_vazifa(5_000))
tracemalloc.stop()
bir_vazifa = xotira / 5_000
print(f" 5 000 vazifa uchun ~{xotira / 1e6:.1f} MB, bittasiga ~{bir_vazifa:.0f} bayt")
print(f" bir vazifa 5 KB dan kam: {bir_vazifa < 5_000}")
print(f" taqqoslash uchun: ip steki sukut bo'yicha kamida 64 KB (OT ga bog'liq)")
print("\n=== 4. Amaliy chegaralar ===")
jadval = [
("jarayon", "yadrolar soni × 1–2", "xotira, kontekst almashinuvi"),
("ip", "yuzlab – minglab", "stek xotirasi, yadro resurslari"),
("korutina", "yuz minglab", "faqat xotira"),
]
for model, chegara, sabab in jadval:
print(f" {model:10} {chegara:24} {sabab}")
print(f" bu mashinada yadrolar: {mp.cpu_count() >= 1}, faol iplar: {threading.active_count()}")Natijaning muhim qismi:
=== 1. 100 birlik yaratish va tugatish ===
korutinalar iplardan tez: True
8 jarayon 100 ipdan ham uzoq vaqt oldi: True
jarayon yaratish korutinadan kamida 20 barobar qimmat (bittasiga): True
=== 2. Miqyos: 10 000 birlik ===
10 000 korutina yaratildi va tugadi: True
bir korutinaga o'rtacha vaqt 100 korutinadagidan yomonlashmadi: True
⚠️ 10 000 ip yaratish ko'p tizimlarda xotira va yadro chegarasiga uriladi
=== 3. Vazifaga qancha xotira ===
5 000 vazifa uchun ~3.7 MB, bittasiga ~746 bayt
bir vazifa 5 KB dan kam: True
taqqoslash uchun: ip steki sukut bo'yicha kamida 64 KB (OT ga bog'liq)
=== 4. Amaliy chegaralar ===
jarayon yadrolar soni × 1–2 xotira, kontekst almashinuvi
ip yuzlab – minglab stek xotirasi, yadro resurslari
korutina yuz minglab faqat xotira
bu mashinada yadrolar: True, faol iplar: 1Nima ko'rsatdi: 2.1, 2.6-bo'limlar.
Misol 3 — Rejalashtirish: majburiy va hamkorlikdagi
"""Iplar o'z-o'zidan almashadi; korutinalar faqat await da; bloklovchi korutina butun siklni to'xtatadi; yechim."""
import asyncio
import threading
import time
def og_ir_hisob(n: int = 300_000) -> int:
s = 0
for i in range(n):
s += i * i
return s
print("=== 1. Iplar: majburiy almashinuv ===")
jurnal: list[str] = []
def ip_ishchisi(nom: str) -> None:
for _ in range(4):
og_ir_hisob()
jurnal.append(nom)
iplar = [threading.Thread(target=ip_ishchisi, args=(nom,)) for nom in "AB"]
for ip in iplar:
ip.start()
for ip in iplar:
ip.join()
almashinuvlar = sum(1 for a, b in zip(jurnal, jurnal[1:]) if a != b)
print(f" har ip {len(jurnal) // 2} bosqichni bajardi, jami yozuvlar: {len(jurnal)}")
print(f" iplar hech qanday 'await' yozmasdan almashdi: {almashinuvlar >= 1}")
print("\n=== 2. Korutinalar: faqat await da ===")
async def yaxshi_ishchi(nom: str, jurnal: list[str]) -> None:
for _ in range(4):
await asyncio.sleep(0) # navbatni boshqalarga berish
jurnal.append(nom)
async def yaxshi() -> list[str]:
j: list[str] = []
await asyncio.gather(yaxshi_ishchi("A", j), yaxshi_ishchi("B", j))
return j
natija = asyncio.run(yaxshi())
print(f" await bilan: {''.join(natija)} — navbatma-navbat")
print("\n=== 3. ⚠️ Bloklovchi korutina ===")
async def bloklovchi_ishchi(nom: str, jurnal: list[str]) -> None:
for _ in range(4):
og_ir_hisob() # await yo'q — sikl to'xtaydi
jurnal.append(nom)
async def bloklovchi() -> list[str]:
j: list[str] = []
await asyncio.gather(bloklovchi_ishchi("A", j), bloklovchi_ishchi("B", j))
return j
natija = asyncio.run(bloklovchi())
print(f" await siz: {''.join(natija)} — A tugamaguncha B boshlanmadi")
print("\n=== 4. Hodisa siklining kechikishi ===")
async def olchov() -> tuple[float, float]:
"""Sikl 'yurak urishi': har 10 ms da belgilanishi kerak."""
kechikishlar: list[float] = []
async def yurak() -> None:
oldingi = time.perf_counter()
for _ in range(20):
await asyncio.sleep(0.01)
hozir = time.perf_counter()
kechikishlar.append(hozir - oldingi - 0.01)
oldingi = hozir
async def bloklovchi_vazifa() -> None:
await asyncio.sleep(0.05)
og_ir_hisob(3_000_000) # ⚠️ siklni bloklaydi
await asyncio.gather(yurak(), bloklovchi_vazifa())
return max(kechikishlar), sum(kechikishlar) / len(kechikishlar)
eng_katta, ortacha = asyncio.run(olchov())
print(f" eng katta kechikish o'rtachadan kamida 5 barobar katta: {eng_katta > ortacha * 5}")
print(f" ⭐ bitta bloklovchi qism butun siklga ta'sir qiladi")
print(f" ✅ yechim: og'ir hisobni asyncio.to_thread yoki jarayonga chiqarish (14.7-dars)")Natijaning muhim qismi:
=== 1. Iplar: majburiy almashinuv ===
har ip 4 bosqichni bajardi, jami yozuvlar: 8
iplar hech qanday 'await' yozmasdan almashdi: True
=== 2. Korutinalar: faqat await da ===
await bilan: ABABABAB — navbatma-navbat
=== 3. ⚠️ Bloklovchi korutina ===
await siz: AAAABBBB — A tugamaguncha B boshlanmadi
=== 4. Hodisa siklining kechikishi ===
eng katta kechikish o'rtachadan kamida 5 barobar katta: True
⭐ bitta bloklovchi qism butun siklga ta'sir qiladi
✅ yechim: og'ir hisobni asyncio.to_thread yoki jarayonga chiqarish (14.7-dars)Nima ko'rsatdi: 2.3-bo'lim.
Misol 4 — Amaliy: ish turiga qarab model tanlash
Xabarnoma servisi: 5 000 ta qurilmaga push yuborish (I/O), keyin hisobotni hisoblash (CPU). Vosita ikkala bosqichni o'lchab, mos modelni tanlaydi va tanlovni asoslaydi.
"""I/O bosqichi: ketma-ket, iplar, korutinalar; CPU bosqichi: iplar va jarayonlar; tanlov va asos."""
import asyncio
import concurrent.futures as cf
import hashlib
import time
QURILMALAR = [f"qurilma-{i:05d}" for i in range(400)]
KUTISH = 0.005
def push_yubor(qurilma: str) -> str:
time.sleep(KUTISH) # tarmoq
return f"{qurilma}:ok"
async def async_push(qurilma: str) -> str:
await asyncio.sleep(KUTISH)
return f"{qurilma}:ok"
def hisobot_bolagi(bolak: list[str]) -> str:
"""Sof Python CPU ish (12.6-dars)."""
h = hashlib.sha256()
for q in bolak:
h.update(q.encode())
for _ in range(200):
h.update(b"x")
return h.hexdigest()[:8]
def ketma_ket_io() -> list[str]:
return [push_yubor(q) for q in QURILMALAR]
def iplar_io(ishchilar: int = 64) -> list[str]:
with cf.ThreadPoolExecutor(max_workers=ishchilar) as ex:
return list(ex.map(push_yubor, QURILMALAR))
def korutinalar_io() -> list[str]:
async def hammasi() -> list[str]:
return list(await asyncio.gather(*(async_push(q) for q in QURILMALAR)))
return asyncio.run(hammasi())
def olch(funksiya, *args) -> tuple[float, object]:
bosh = time.perf_counter()
natija = funksiya(*args)
return time.perf_counter() - bosh, natija
if __name__ == "__main__":
print("=== 1. I/O bosqichi ===")
t_ket, n_ket = olch(ketma_ket_io)
t_ip, n_ip = olch(iplar_io)
t_kor, n_kor = olch(korutinalar_io)
print(f" {len(QURILMALAR)} ta 'so'rov', har biri {KUTISH * 1000:.0f} ms")
print(f" natijalar bir xil: {n_ket == n_ip == n_kor}")
print(f" iplar ketma-ketdan kamida 5 barobar tez: {t_ket / t_ip >= 5}")
print(f" korutinalar ham: {t_ket / t_kor >= 5}")
print(f" korutinalar 400 ta 'ulanish' uchun atigi bitta ip ishlatdi")
print("\n=== 2. CPU bosqichi ===")
bolaklar = [QURILMALAR[i::4] for i in range(4)]
t_ket_cpu, _ = olch(lambda: [hisobot_bolagi(b) for b in bolaklar])
with cf.ThreadPoolExecutor(max_workers=4) as ex:
t_ip_cpu, _ = olch(lambda: list(ex.map(hisobot_bolagi, bolaklar)))
with cf.ProcessPoolExecutor(max_workers=4) as pex:
list(pex.map(hisobot_bolagi, bolaklar[:1])) # isitish
t_jar_cpu, _ = olch(lambda: list(pex.map(hisobot_bolagi, bolaklar)))
print(f" iplar ketma-ketga yaqin (GIL): {t_ip_cpu > t_ket_cpu * 0.7}")
print(f" jarayonlar iplardan tez: {t_jar_cpu < t_ip_cpu}")
print("\n=== 3. Tanlov ===")
tanlovlar = [
("I/O, minglab ulanish", "korutinalar (asyncio)", "eng arzon, bitta ip"),
("I/O, bloklovchi kutubxona", "iplar", "kod o'zgarmaydi"),
("sof Python CPU", "jarayonlar", "GIL ni chetlab o'tadi"),
("C kutubxona (NumPy, hashlib katta bufer)", "iplar", "GIL bo'shatiladi"),
("ishonchsiz kod", "jarayonlar", "izolyatsiya"),
]
for ish, model, sabab in tanlovlar:
print(f" {ish:42} → {model:22} ({sabab})")
print("\n=== 4. Ushbu servis uchun ===")
print(" push yuborish → asyncio (5 000 ulanish, har biri kutish bilan)")
print(" hisobot → ProcessPoolExecutor (sof Python hisob)")
print(" ⭐ bitta ilovada ikki model birga ishlatiladi (14.8-dars)")Natijaning muhim qismi:
=== 1. I/O bosqichi ===
400 ta 'so'rov', har biri 5 ms
natijalar bir xil: True
iplar ketma-ketdan kamida 5 barobar tez: True
korutinalar ham: True
korutinalar 400 ta 'ulanish' uchun atigi bitta ip ishlatdi
=== 2. CPU bosqichi ===
iplar ketma-ketga yaqin (GIL): True
jarayonlar iplardan tez: False
=== 3. Tanlov ===
I/O, minglab ulanish → korutinalar (asyncio) (eng arzon, bitta ip)
I/O, bloklovchi kutubxona → iplar (kod o'zgarmaydi)
sof Python CPU → jarayonlar (GIL ni chetlab o'tadi)
C kutubxona (NumPy, hashlib katta bufer) → iplar (GIL bo'shatiladi)
ishonchsiz kod → jarayonlar (izolyatsiya)
=== 4. Ushbu servis uchun ===
push yuborish → asyncio (5 000 ulanish, har biri kutish bilan)
hisobot → ProcessPoolExecutor (sof Python hisob)
⭐ bitta ilovada ikki model birga ishlatiladi (14.8-dars)Nima ko'rsatdi: 2.1, 2.2, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"asyncio — parallellik" |
Konkurentlik: bitta ip, await da almashinuv |
| "Iplar har doim korutinalardan sekin" | I/O da farq kichik; minglab ulanishda korutinalar yutadi |
| "Har ulanishga bitta ip — normal" | 10 000 ulanishda xotira va almashinuv narxi ortadi |
"Korutinada time.sleep ishlaydi" |
Ishlaydi, lekin butun siklni bloklaydi |
| "Jarayonlar har doim tezroq" | Yaratish va ma'lumot uzatish qimmat (12.6-dars) |
| "Iplar CPU ishni tezlashtiradi" | Sof Python kodda — yo'q (GIL) |
| "Korutinalarda poyga holati yo'q" | Kamroq, lekin await orasida holat o'zgarishi mumkin |
| "Model tanlash — did masalasi" | O'lchov masalasi: ish turi va miqyosga bog'liq |
6. Keng tarqalgan xatolar va yechimlari
1. Har ulanishga ip
for ulanish in ulanishlar: # ❌ 10 000 ip
threading.Thread(target=xizmat, args=(ulanish,)).start() Pul (ThreadPoolExecutor) yoki asyncio.
2. Korutinada bloklovchi chaqiruv
async def yukla(u: str) -> str:
return requests.get(u).text # ❌ butun siklni to'xtatadi
return await mijoz.get(u) # ✅ async kutubxona
return await asyncio.to_thread(requests.get, u) # ✅ iplarga chiqarish3. CPU ishni asyncio ga berish
async def hisobla() -> int:
return og_ir_hisob() # ❌ sikl bloklanadi ProcessPoolExecutor yoki asyncio.to_thread (GIL bo'shatuvchi kod uchun).
4. Jarayonlarni mayda ish uchun ishlatish
with ProcessPoolExecutor() as ex:
ex.map(kichik_funksiya, 100_000 * [...]) # ❌ uzatish narxi (12.6-dars)5. Modellarni chalkashtirish
threading.Thread(target=async_funksiya) # ❌ korutina chaqirilmaydi
asyncio.run(async_funksiya()) # ✅6. Umumiy holatni qulfsiz o'zgartirish (iplar)
self.hisob += 1 # ❌ 12.5-dars
with self.qulf: self.hisob += 1 # ✅7. Jarayonlar orasida obyektlarni "ulashishga" urinish
umumiy = {} # ❌ har jarayonda o'z nusxasi
manager = multiprocessing.Manager() # ✅ yoki navbat/shared_memory (14.4-dars)8. asyncio.run ni sikl ichida chaqirish
for u in manzillar:
asyncio.run(yukla(u)) # ❌ har safar yangi sikl, konkurentlik yo'q
asyncio.run(hammasi(manzillar)) # ✅ bitta sikl, gather bilan7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 12.3-dars (o'tilgan): freymlar — korutinalar to'xtatilgan freym
- 12.5–12.6-darslar (o'tilgan): GIL va strategiyalar
- 14.2–14.3-darslar:
threading, poyga holatlari, qulflar - 14.4–14.5-darslar:
multiprocessing, pullar - 14.6–14.7-darslar:
asynciovaasync/await - 14.8-dars: tanlov qoidalari
- 20-qism: veb-serverlar — har so'rov uchun model
- 29-qism: miqyoslash va yuk testi
8. Eng yaxshi amaliyotlar
Ish turini aniqlang (I/O yoki CPU) — keyin model tanlang.
Minglab ulanish — korutinalar.
Bloklovchi kutubxonalar — iplar (yoki
asyncio.to_thread).Sof Python CPU — jarayonlar.
Korutinada hech qachon bloklovchi chaqiruv qoldirmang.
Loglarda PID, ip va vazifa nomini chiqaring.
Pul (pool) ishlating — cheksiz ip/jarayon yaratmang.
Tanlovni o'lchov bilan asoslang, taxmin bilan emas.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
import asyncio
import os
import threading
1. print(threading.current_thread().name)
2. print(threading.active_count())
3. ip = threading.Thread(target=lambda: None)
print(ip.is_alive())
4. ip.start(); ip.join()
print(ip.is_alive())
5. print(type(os.getpid()).__name__)
6. async def f():
return 42
print(asyncio.run(f()))
7. async def g():
return asyncio.current_task().get_name()
print(asyncio.run(g()).startswith("Task"))
8. async def h():
await asyncio.sleep(0)
return "tayyor"
print(asyncio.run(h()))
9. print(asyncio.iscoroutinefunction(h), asyncio.iscoroutinefunction(print))
10. c = h()
print(type(c).__name__)
c.close()
11. import multiprocessing as mp
print(mp.cpu_count() >= 1)
12. print(threading.get_ident() == threading.main_thread().ident)Javoblar
MainThread1False— hali ishga tushmaganFalse— tugaganint42True— sukut nomlarTask-1ko'rinishidatayyorTrue FalsecoroutineTrueTrue
Vazifa 2: Xatolarni tuzating
1. for ulanish in ulanishlar: # 5000 ta
threading.Thread(target=xizmat, args=(ulanish,)).start()
2. async def yukla(manzil):
return requests.get(manzil).text
3. async def hisobot(malumot):
return og_ir_hisob(malumot)
4. for manzil in manzillar:
natija = asyncio.run(yukla(manzil))
5. ip = threading.Thread(target=async_funksiya())
ip.start()Javoblar
1. with ThreadPoolExecutor(max_workers=64) as ex: # yoki asyncio
list(ex.map(xizmat, ulanishlar))
2. async def yukla(manzil: str) -> str:
async with httpx.AsyncClient() as mijoz: # async kutubxona
javob = await mijoz.get(manzil)
return javob.text
# yoki: return await asyncio.to_thread(lambda: requests.get(manzil).text)
3. async def hisobot(malumot: bytes) -> str:
sikl = asyncio.get_running_loop()
with ProcessPoolExecutor() as pul: # CPU ishni jarayonga
return await sikl.run_in_executor(pul, og_ir_hisob, malumot)
4. async def hammasi(manzillar: list[str]) -> list[str]:
return list(await asyncio.gather(*(yukla(m) for m in manzillar)))
natijalar = asyncio.run(hammasi(manzillar)) # bitta sikl
5. asyncio.run(async_funksiya()) # korutina — sikl bilan
# yoki ipda: threading.Thread(target=asyncio.run, args=(async_funksiya(),))Vazifa 3: O'lchov jadvali
- 10, 100, 1 000, 10 000 birlik uchun iplar va korutinalar yaratish vaqtini o'lchang
- Har biri uchun xotira sarfini
tracemallocbilan qayd eting - Qaysi nuqtada iplar amaliy bo'lmay qolishini aniqlang
- Natijalarni jadval va qisqa xulosa qilib rasmiylashtiring
Vazifa 4: Hodisa sikli kechikishi
asyncioda "yurak urishi" vazifasini yozing (har 10 ms)- Uning yonida turli davomiylikdagi bloklovchi funksiyalarni ishga tushiring
- Kechikish taqsimotini (min, o'rtacha, p95, maks) chiqaring
- Bloklovchi qismni
asyncio.to_threadga chiqarib, farqni ko'rsating - Qaysi davomiylikdan boshlab bloklash sezilarli bo'lishini aniqlang
Vazifa 5: Identifikatorli log
- Har log yozuvida PID, ip nomi va vazifa nomini chiqaradigan formatter yozing
- Uni uch modelda ham (iplar, jarayonlar, korutinalar) sinang
- Yozuvlarni fayldan o'qib, model bo'yicha statistika chiqaring
contextvarsbilan so'rov identifikatorini uzatishni qo'shing (14.7-darsga tayyorgarlik)
Vazifa 6: Aralash quvur
Rasm servisini yozing:
- Rasmlarni "yuklab olish" (I/O,
asyncio) - Har rasm uchun "eskiz" yasash (CPU, jarayonlar)
- Natijani "saqlash" (I/O)
- Uch bosqichni bitta quvurda birlashtiring va umumiy vaqtni o'lchang
- Har bosqich uchun model tanlovini asoslang
Vazifa 7: O'ylash
Go'da goroutine — til darajasidagi yengil oqim (M:N rejalashtiruvchi bilan), Erlang'da jarayonlar juda arzon va izolyatsiyalangan, Java 21 da virtual iplar paydo bo'ldi, Node.js esa bitta ipli hodisa sikliga tayanadi. Python'dagi uch model shu yondashuvlar bilan qanday taqqoslanadi va nega Python'da "yengil oqim" til darajasida yo'q?
Javob
Qisqa javob: Go, Erlang va Java 21 ish vaqti (runtime) darajasida yengil oqimlarni qo'llab-quvvatlaydi: dasturchi oddiy bloklovchi kod yozadi, rejalashtiruvchi esa uni avtomatik almashtiradi. Python'da esa bu kutubxona darajasida (asyncio) amalga oshirilgan, shuning uchun async/await sintaksisi va "rangli funksiyalar" muammosi paydo bo'ladi.
1. Taqqoslash
| Platforma | Yengil birlik | Rejalashtirish | Kod uslubi |
|---|---|---|---|
| Go | Goroutine | M:N, runtime | Oddiy bloklovchi kod |
| Erlang | Jarayon (~300 bayt) | Preemptive, runtime | Xabar almashish |
| Java 21+ | Virtual ip | M:N, JVM | Oddiy bloklovchi kod |
| Node.js | Callback/Promise | Bitta ipli sikl | async/await |
| Python | Korutina | Hamkorlikdagi, kutubxona | async/await |
2. "Rangli funksiyalar" muammosi
def oddiy(): ...
async def korutina(): ...
await oddiy() # ❌ mumkin emas
korutina() # ⚠️ hech narsa bajarilmaydi — await kerak- Async funksiyani faqat async kontekstda chaqirish mumkin — kod "ikki rangga" bo'linadi
- Kutubxonalar ham ikki nusxada bo'ladi (
requestsvahttpx,psycopgvaasyncpg) - Go va Java'da bu muammo yo'q: bir xil kod ikkala rejimda ishlaydi
3. Nega Python'da til darajasida yo'q
- Moslik: C API va mavjud kod bloklovchi stekka tayanadi; virtual iplarni qo'shish uni buzardi
- CPython arxitekturasi: har korutina uchun C stekini saqlash kerak bo'lardi (12.3-dars)
- Tarix:
asyncio2014-yilda kutubxona sifatida qo'shildi va PEP 492 bilan sintaksis oldi - Muqobil yo'l: 3.14 dagi erkin ipli yig'ma va arzonlashgan iplar — bloklovchi kodni saqlab qolgan holda miqyosni oshirish urinishi
4. Amaliy xulosa
- Python'da model tanlash — dasturchi zimmasida (bu darsning mavzusi)
asyncioekotizimini tanlasangiz, butun zanjir async bo'lishi kerak- Bloklovchi kutubxonalarni
asyncio.to_threadbilan ulash mumkin — "ko'prik" naqshi - Kelajakda erkin ipli yig'ma bloklovchi uslubni yana jozibador qilishi mumkin
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda konkurentlikning uch modelini taqqosladik.
Eng muhim uch fikr:
Uch model — uch xil narx. Jarayon o'z manzil fazosiga ega (izolyatsiya va haqiqiy parallellik, lekin millisekundlarda yaratiladi va ma'lumot uzatish qimmat); ip umumiy xotirada ishlaydi (arzonroq, lekin qulflar kerak va GIL tufayli Python kodi parallel emas); korutina esa eng arzoni (~1 KB, mikrosoniyalarda) — minglab ulanish uchun yagona amaliy yo'l.
Konkurentlik — parallellik emas.
asynciobitta ipda ishlaydi va faqat kutish vaqtini foydali ishga aylantiradi. Iplar majburiy almashadi (kod hech narsa yozmasa ham), korutinalar esa faqatawaitda — shuning uchun bitta bloklovchi chaqiruv butun siklni to'xtatadi.Model ish turiga qarab tanlanadi. Minglab tarmoq so'rovi — korutinalar; bloklovchi kutubxona bilan I/O — iplar; sof Python CPU hisob — jarayonlar; ishonchsiz yoki yiqiluvchan kod — yana jarayonlar (izolyatsiya uchun). Bitta ilovada bir nechta model birga ishlatilishi normal holat.
Keyingi darsda iplar bilan amaliy ishlashni boshlaymiz: threading moduli, ip yaratish va kutish, daemon iplar, ThreadPoolExecutor va iplar bilan ishlashning xavfsiz naqshlari.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!