IlmHamroh
Python kursi/Konkurentlik va parallellik7/8-dars22 daqiqa
Mundarija (21)

14.7-dars: async / await amaliyoti

14-QISM — KONKURENTLIK VA PARALLELLIK · 7-dars


1. Kirish va motivatsiya

14.6-darsda hodisa siklining ichki mexanizmini ko'rdik. Endi savol boshqa: haqiqiy asinxron dastur qanday quriladi?

Faqat async def va await yetmaydi. Real dasturda yana:

  • ma'lumot oqimini asinxron iterator bilan o'qish,
  • ulanishlarni asinxron kontekst menejer bilan ochib-yopish,
  • ishlab chiqaruvchi va iste'molchini asyncio.Queue bilan bog'lash,
  • umumiy holatni Lock, Event, Semaphore bilan himoya qilish,
  • tarmoq bilan stream lar orqali gaplashish kerak.

Real vaziyat. Jamoa asinxron botni yozdi va "asyncio'da poyga holati bo'lmaydi, chunki bitta ip" deb o'yladi. Ishlab chiqarishda foydalanuvchi balanslari tasodifiy kamayib ketdi: balans = await ol(); await saqla(balans + summa) — ikki await orasida boshqa vazifa o'sha balansni o'zgartirib ulgurgan edi. Bitta asyncio.Lock muammoni hal qildi.

Bu darsda asinxron dasturning "g'ishtlari"ni o'rganamiz va oxirida ulardan haqiqiy TCP server quramiz.

Bu darsda:

  • Asinxron iterator va generator: async for, anext, aclosing
  • Asinxron kontekst menejer: __aenter__/__aexit__, asynccontextmanager
  • asyncio.Queue: ishlab chiqaruvchi → iste'molchi quvuri
  • asyncio'dagi poyga holatlari va Lock, Event, Semaphore
  • Stream lar: start_server, open_connection
  • Asinxron subprocess
  • Amaliy: kalit-qiymat TCP serveri

2. Nazariya — chuqur tushuntirish

2.1. Asinxron iteratsiya

Sinxron Asinxron
__iter__ / __next__ __aiter__ / __anext__
for x in it async for x in it
next(it) await anext(it)
StopIteration StopAsyncIteration
[x for x in it] [x async for x in it]

Asinxron generator — async def ichida yield:

python
async def satrlar(ulanish):
    while (satr := await ulanish.readline()):
        yield satr.decode()

async for satr in satrlar(ulanish):
    ...

Asinxron generatorni o'rtada tashlab ketsangiz, uning finally bloki darhol bajarilmasligi mumkin. Tozalashni kafolatlash uchun:

python
async with contextlib.aclosing(satrlar(ulanish)) as oqim:
    async for satr in oqim:
        if satr == "STOP":
            break                    # finally shu yerda bajariladi

2.2. Asinxron kontekst menejer

python
class Ulanish:
    async def __aenter__(self):
        await self.och()
        return self
    async def __aexit__(self, tur, qiymat, iz):
        await self.yop()
        return False                 # istisnoni yutmaymiz

@contextlib.asynccontextmanager
async def tranzaksiya(db):
    await db.boshla()
    try:
        yield db
        await db.tasdiqla()
    except BaseException:
        await db.bekor_qil()
        raise
Vosita Qachon
Klass (__aenter__/__aexit__) Holatli, qayta ishlatiladigan resurs
@asynccontextmanager Qisqa, bir martalik ochish-yopish
AsyncExitStack Dinamik sonli resurslar

2.3. asyncio.Queue

python
navbat: asyncio.Queue[int] = asyncio.Queue(maxsize=100)
await navbat.put(x)          # to'lsa kutadi — ⭐ tabiiy orqa bosim
x = await navbat.get()       # bo'sh bo'lsa kutadi
navbat.task_done()
await navbat.join()          # hamma element ishlanguncha kutadi
Tur Tartib
Queue FIFO
LifoQueue LIFO
PriorityQueue Eng kichik birinchi

maxsize — orqa bosim (backpressure): ishlab chiqaruvchi iste'molchidan tez bo'lsa, put kutib qoladi va xotira cheksiz o'smaydi.

asyncio.Queue ip-xavfsiz emas — faqat bitta loop ichida ishlating. Iplar orasida — queue.Queue (14.3-dars).

2.4. asyncio'da poyga holatlari

"Bitta ip — poyga yo'q" — noto'g'ri. Poyga har bir await da yuz berishi mumkin:

python
balans = hisob.balans          # o'qish
await asyncio.sleep(0)         # ⚠️ shu yerda boshqa vazifa ishlaydi
hisob.balans = balans + 100    # eskirgan qiymat ustiga yozish
Holat Poyga bormi
await siz ketma-ket qatorlar Yo'q — atomar
O'qish → await → yozish Bor
Faqat bitta await siz o'zgartirish (x += 1) Yo'q

Iplardan farqi: xavfli joylar ko'rinib turadi — ular faqat await lar.

2.5. Sinxronlash vositalari

Vosita Vazifasi
asyncio.Lock Bir vaqtda bitta vazifa
asyncio.Semaphore(n) Bir vaqtda ko'pi bilan n ta
asyncio.BoundedSemaphore(n) Ortiqcha release da xato beradi
asyncio.Event "Signal berildi" belgisi — ko'p vazifani uyg'otadi
asyncio.Condition Shart bajarilishini kutish
asyncio.Barrier(n) n vazifa yig'ilguncha kutish (3.11+)

asyncio.Lock va threading.Lock almashtirib bo'lmaydi: threading.Lock loopni bloklaydi.

2.6. Stream lar — tarmoq

python
# Server
async def ishlovchi(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
    satr = await reader.readline()
    writer.write(javob)
    await writer.drain()          # ⭐ bufer to'lsa kutadi
    writer.close()
    await writer.wait_closed()

server = await asyncio.start_server(ishlovchi, "127.0.0.1", 8000)
async with server:
    await server.serve_forever()

# Mijoz
reader, writer = await asyncio.open_connection("127.0.0.1", 8000)
Metod Izoh
reader.readline() \n gacha; ulanish yopilsa b""
reader.readexactly(n) Aynan n bayt
writer.write(b) Buferga yozadi (kutmaydi)
await writer.drain() Orqa bosim — buferni bo'shatishni kutadi

2.7. Asinxron subprocess

python
jarayon = await asyncio.create_subprocess_exec(
    "git", "status", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
chiqish, xato = await jarayon.communicate()

Loopni bloklamasdan tashqi dasturlarni ishga tushirish — o'nlab buyruqni parallel bajarish uchun qulay (26-qism).


3. Tez ma'lumotnoma

python
async for x in asinxron_generator(): ...
async with contextlib.aclosing(gen()) as g: ...
async with Resurs() as r: ...

navbat = asyncio.Queue(maxsize=100)
await navbat.put(x); x = await navbat.get(); navbat.task_done(); await navbat.join()

qulf = asyncio.Lock()
async with qulf: ...

reader, writer = await asyncio.open_connection(host, port)
server = await asyncio.start_server(ishlovchi, host, port)

Qoidalar

har await — navbat almashish nuqtasi
o'qish → await → yozish = poyga → asyncio.Lock
Queue(maxsize) — orqa bosim
writer.write dan keyin await writer.drain()
asinxron generator tozalanishi — aclosing

4. Batafsil misollar

Misol 1 — Asinxron iterator va kontekst menejer

python
"""async for va asinxron generator; anext; asinxron comprehension; __aenter__/__aexit__; asynccontextmanager; aclosing."""

import asyncio
import contextlib
from collections.abc import AsyncIterator


async def sanoq(n: int) -> AsyncIterator[int]:
    for i in range(n):
        await asyncio.sleep(0)                  # masalan, tarmoqdan keyingi bo'lakni kutish
        yield i


class Sahifalar:
    """Qo'lda yozilgan asinxron iterator: API ni sahifama-sahifa o'qiydi."""

    def __init__(self, jami: int, hajm: int) -> None:
        self.jami, self.hajm, self.joriy = jami, hajm, 0

    def __aiter__(self) -> "Sahifalar":
        return self

    async def __anext__(self) -> list[int]:
        if self.joriy >= self.jami:
            raise StopAsyncIteration
        await asyncio.sleep(0)
        sahifa = list(range(self.joriy, min(self.joriy + self.hajm, self.jami)))
        self.joriy += self.hajm
        return sahifa


class Ulanish:
    def __init__(self, jurnal: list[str]) -> None:
        self.jurnal = jurnal

    async def __aenter__(self) -> "Ulanish":
        await asyncio.sleep(0)
        self.jurnal.append("ochildi")
        return self

    async def __aexit__(self, tur: type[BaseException] | None, *_: object) -> bool:
        await asyncio.sleep(0)
        self.jurnal.append(f"yopildi (xato bilan: {tur is not None})")
        return False


@contextlib.asynccontextmanager
async def tranzaksiya(jurnal: list[str]) -> AsyncIterator[str]:
    jurnal.append("BEGIN")
    try:
        yield "tx-1"
        jurnal.append("COMMIT")
    except BaseException:
        jurnal.append("ROLLBACK")
        raise


async def main() -> None:
    print("=== 1. Asinxron generator ===")
    print(f"  async comprehension: {[x async for x in sanoq(5)]}")
    oqim = sanoq(2)
    print(f"  anext: {await anext(oqim)}, {await anext(oqim)}")
    try:
        await anext(oqim)
    except StopAsyncIteration:
        print("  tugagach: StopAsyncIteration")
    print(f"  turi: {type(sanoq(1)).__name__}")

    print("\n=== 2. Qo'lda yozilgan asinxron iterator ===")
    async for sahifa in Sahifalar(jami=7, hajm=3):
        print(f"  sahifa: {sahifa}")

    print("\n=== 3. __aenter__ / __aexit__ ===")
    jurnal: list[str] = []
    async with Ulanish(jurnal):
        jurnal.append("ishlatildi")
    try:
        async with Ulanish(jurnal):
            raise ValueError("so'rov buzilgan")
    except ValueError:
        jurnal.append("xato tashqarida ushlandi")
    for qadam in jurnal:
        print(f"  {qadam}")

    print("\n=== 4. asynccontextmanager ===")
    for buzilsinmi in (False, True):
        jurnal = []
        try:
            async with tranzaksiya(jurnal) as tx:
                jurnal.append(f"{tx}: yozildi")
                if buzilsinmi:
                    raise RuntimeError("disk to'ldi")
        except RuntimeError:
            pass
        print(f"  xato={buzilsinmi}: {jurnal}")

    print("\n=== 5. aclosing: generator tozalanishi ===")
    tozalash: list[str] = []

    async def fayl_oqimi() -> AsyncIterator[int]:
        try:
            for i in range(1_000):
                yield i
        finally:
            tozalash.append("fayl yopildi")

    async with contextlib.aclosing(fayl_oqimi()) as satrlar:
        async for satr in satrlar:
            if satr == 2:
                break
    print(f"  break dan keyin darhol: {tozalash}")
    print("  ⭐ aclosing — finally bloki kafolatlangan vaqtda bajariladi")


if __name__ == "__main__":
    asyncio.run(main())

Natijaning muhim qismi:

text
=== 1. Asinxron generator ===
  async comprehension: [0, 1, 2, 3, 4]
  anext: 0, 1
  tugagach: StopAsyncIteration
  turi: async_generator

=== 2. Qo'lda yozilgan asinxron iterator ===
  sahifa: [0, 1, 2]
  sahifa: [3, 4, 5]
  sahifa: [6]

=== 3. __aenter__ / __aexit__ ===
  ochildi
  ishlatildi
  yopildi (xato bilan: False)
  ochildi
  yopildi (xato bilan: True)
  xato tashqarida ushlandi

=== 4. asynccontextmanager ===
  xato=False: ['BEGIN', 'tx-1: yozildi', 'COMMIT']
  xato=True: ['BEGIN', 'tx-1: yozildi', 'ROLLBACK']

=== 5. aclosing: generator tozalanishi ===
  break dan keyin darhol: ['fayl yopildi']
  ⭐ aclosing — finally bloki kafolatlangan vaqtda bajariladi

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — asyncio.Queue bilan quvur

python
"""Ishlab chiqaruvchi → iste'molchilar; task_done/join; maxsize bilan orqa bosim; to'xtatish signali; PriorityQueue."""

import asyncio
import time

TUGADI = None


async def ishlab_chiqaruvchi(navbat: asyncio.Queue[int | None], soni: int, jurnal: list[str]) -> None:
    for i in range(soni):
        await navbat.put(i)                              # to'lsa shu yerda kutadi
        jurnal.append(f"put {i} (navbatda {navbat.qsize()})")


async def iste_molchi(nom: str, navbat: asyncio.Queue[int | None], natijalar: dict[str, list[int]]) -> None:
    while True:
        element = await navbat.get()
        try:
            if element is TUGADI:
                return
            await asyncio.sleep(0.01)                    # sekin ishlov
            natijalar.setdefault(nom, []).append(element * element)
        finally:
            navbat.task_done()


async def quvur() -> None:
    navbat: asyncio.Queue[int | None] = asyncio.Queue(maxsize=4)
    natijalar: dict[str, list[int]] = {}
    jurnal: list[str] = []
    iste_molchilar = [asyncio.create_task(iste_molchi(f"ishchi-{i}", navbat, natijalar)) for i in range(3)]

    bosh = time.perf_counter()
    await ishlab_chiqaruvchi(navbat, 30, jurnal)
    await navbat.join()                                  # ⭐ hamma element ishlandi
    oraliq = time.perf_counter() - bosh

    for _ in iste_molchilar:
        await navbat.put(TUGADI)                         # har ishchiga to'xtash signali
    await asyncio.gather(*iste_molchilar)

    hammasi = sorted(x for royxat in natijalar.values() for x in royxat)
    print(f"  30 element ishlandi: {hammasi == [i * i for i in range(30)]}")
    print(f"  ishni 3 ishchi bo'lishdi: {len(natijalar) == 3}")
    print(f"  ketma-ket 0.3 s bo'lardi, parallel 0.2 s dan kam: {oraliq < 0.2}")
    eng_kop = max(int(qator.split("navbatda ")[1].rstrip(")")) for qator in jurnal)
    print(f"  navbatda eng ko'p element: {eng_kop} (maxsize=4)")
    print(f"  ⭐ orqa bosim ishladi — navbat chegaradan oshmadi: {eng_kop <= 4}")
    print(f"  ishchilar to'xtadi: {all(t.done() for t in iste_molchilar)}")


async def ustuvorlik() -> None:
    navbat: asyncio.PriorityQueue[tuple[int, str]] = asyncio.PriorityQueue()
    for vazifa in [(3, "hisobot"), (1, "to'lov"), (2, "xabarnoma"), (1, "xavfsizlik")]:
        await navbat.put(vazifa)
    tartib = [(await navbat.get())[1] for _ in range(navbat.qsize())]
    print(f"  bajarilish tartibi: {tartib}")
    print("  ⭐ bir xil ustuvorlikda ikkinchi element (satr) solishtiriladi")


async def main() -> None:
    print("=== 1. Quvur: 1 ishlab chiqaruvchi → 3 iste'molchi ===")
    await quvur()
    print("\n=== 2. PriorityQueue ===")
    await ustuvorlik()


if __name__ == "__main__":
    asyncio.run(main())

Natijaning muhim qismi:

text
=== 1. Quvur: 1 ishlab chiqaruvchi → 3 iste'molchi ===
  30 element ishlandi: True
  ishni 3 ishchi bo'lishdi: True
  ketma-ket 0.3 s bo'lardi, parallel 0.2 s dan kam: True
  navbatda eng ko'p element: 4 (maxsize=4)
  ⭐ orqa bosim ishladi — navbat chegaradan oshmadi: True
  ishchilar to'xtadi: True

=== 2. PriorityQueue ===
  bajarilish tartibi: ["to'lov", 'xavfsizlik', 'xabarnoma', 'hisobot']
  ⭐ bir xil ustuvorlikda ikkinchi element (satr) solishtiriladi

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — asyncio'dagi poyga va sinxronlash

python
"""await orasidagi poyga; Lock bilan tuzatish; await siz kod atomar; Event; Semaphore; Condition."""

import asyncio


class Hisob:
    def __init__(self) -> None:
        self.balans = 0
        self.qulf = asyncio.Lock()


async def xavfli_toldirish(hisob: Hisob) -> None:
    for _ in range(1_000):
        joriy = hisob.balans
        await asyncio.sleep(0)                  # masalan, bazaga yozish
        hisob.balans = joriy + 1


async def xavfsiz_toldirish(hisob: Hisob) -> None:
    for _ in range(1_000):
        async with hisob.qulf:
            joriy = hisob.balans
            await asyncio.sleep(0)
            hisob.balans = joriy + 1


async def awaitsiz_toldirish(hisob: Hisob) -> None:
    for _ in range(1_000):
        hisob.balans += 1                       # await yo'q — atomar
        if _ % 100 == 0:
            await asyncio.sleep(0)


async def poyga() -> None:
    for nom, funksiya in [("xavfli", xavfli_toldirish), ("Lock bilan", xavfsiz_toldirish),
                          ("await siz", awaitsiz_toldirish)]:
        hisob = Hisob()
        await asyncio.gather(*(funksiya(hisob) for _ in range(5)))
        print(f"  {nom:11} → balans {hisob.balans:>5} / 5000, yo'qolgan: {5_000 - hisob.balans}")
    print("  ⚠️ bitta ip ham poygadan himoya qilmaydi — xavf await da")


async def hodisa() -> None:
    tayyor = asyncio.Event()
    uyg_ongan: list[int] = []

    async def kutuvchi(n: int) -> None:
        await tayyor.wait()
        uyg_ongan.append(n)

    vazifalar = [asyncio.create_task(kutuvchi(i)) for i in range(4)]
    await asyncio.sleep(0.01)
    print(f"  set() dan oldin uyg'onganlar: {uyg_ongan}")
    tayyor.set()
    await asyncio.gather(*vazifalar)
    print(f"  set() dan keyin: {sorted(uyg_ongan)} — hammasi bir signal bilan")


async def semafor() -> None:
    chegara = asyncio.Semaphore(3)
    faol = eng_kop = 0

    async def yukla(i: int) -> int:
        nonlocal faol, eng_kop
        async with chegara:
            faol += 1
            eng_kop = max(eng_kop, faol)
            await asyncio.sleep(0.01)
            faol -= 1
            return i

    natijalar = await asyncio.gather(*(yukla(i) for i in range(12)))
    print(f"  12 vazifa bajarildi: {natijalar == list(range(12))}")
    print(f"  bir vaqtda eng ko'p faol: {eng_kop} (Semaphore(3))")


async def shart() -> None:
    shart_obyekt = asyncio.Condition()
    ombor: list[str] = []
    olinganlar: list[str] = []

    async def xaridor(nom: str) -> None:
        async with shart_obyekt:
            await shart_obyekt.wait_for(lambda: len(ombor) > 0)
            olinganlar.append(f"{nom}:{ombor.pop(0)}")

    xaridorlar = [asyncio.create_task(xaridor(f"x{i}")) for i in range(2)]
    await asyncio.sleep(0.01)
    for mahsulot in ("olma", "nok"):
        async with shart_obyekt:
            ombor.append(mahsulot)
            shart_obyekt.notify()
        await asyncio.sleep(0.01)
    await asyncio.gather(*xaridorlar)
    print(f"  olingan mahsulotlar: {sorted(o.split(':')[1] for o in olinganlar)}")
    print(f"  ombor bo'sh: {ombor == []}")


async def main() -> None:
    print("=== 1. Poyga holati ===")
    await poyga()
    print("\n=== 2. Event ===")
    await hodisa()
    print("\n=== 3. Semaphore ===")
    await semafor()
    print("\n=== 4. Condition ===")
    await shart()


if __name__ == "__main__":
    asyncio.run(main())

Natijaning muhim qismi:

text
=== 1. Poyga holati ===
  xavfli      → balans  1000 / 5000, yo'qolgan: 4000
  Lock bilan  → balans  5000 / 5000, yo'qolgan: 0
  await siz   → balans  5000 / 5000, yo'qolgan: 0
  ⚠️ bitta ip ham poygadan himoya qilmaydi — xavf await da

=== 2. Event ===
  set() dan oldin uyg'onganlar: []
  set() dan keyin: [0, 1, 2, 3] — hammasi bir signal bilan

=== 3. Semaphore ===
  12 vazifa bajarildi: True
  bir vaqtda eng ko'p faol: 3 (Semaphore(3))

=== 4. Condition ===
  olingan mahsulotlar: ['nok', 'olma']
  ombor bo'sh: True

Nima ko'rsatdi: 2.4, 2.5-bo'limlar.

Misol 4 — Amaliy: kalit-qiymat TCP serveri

Hamma g'ishtlarni birlashtiramiz: haqiqiy TCP server (127.0.0.1, operatsion tizim tanlagan port), matnli protokol (SET, GET, DEL, KEYS), umumiy ombor uchun Lock, ulanishlar soni uchun Semaphore, bo'sh turgan mijozlar uchun muddat, bir vaqtda ishlaydigan mijozlar va xushmuomala to'xtatish.

python
"""asyncio stream lari bilan haqiqiy TCP server; protokol; Lock; ulanish chegarasi; muddat; parallel mijozlar; subprocess mijoz."""

import asyncio
import sys


class KVServer:
    def __init__(self, maks_ulanish: int = 20, bosh_turish: float = 0.3) -> None:
        self.ombor: dict[str, str] = {}
        self.qulf = asyncio.Lock()
        self.chegara = asyncio.Semaphore(maks_ulanish)
        self.bosh_turish = bosh_turish
        self.statistika = {"ulanishlar": 0, "buyruqlar": 0, "muddat": 0}

    async def bajar(self, satr: str) -> str:
        qismlar = satr.split(maxsplit=2)
        if not qismlar:
            return "XATO bo'sh buyruq"
        buyruq, *argumentlar = qismlar
        async with self.qulf:
            match buyruq.upper(), argumentlar:
                case "SET", [kalit, qiymat]:
                    self.ombor[kalit] = qiymat
                    return "OK"
                case "GET", [kalit]:
                    return self.ombor.get(kalit, "YO'Q")
                case "DEL", [kalit]:
                    return "OK" if self.ombor.pop(kalit, None) is not None else "YO'Q"
                case "KEYS", []:
                    return " ".join(sorted(self.ombor)) or "(bo'sh)"
                case _:
                    return f"XATO noma'lum buyruq: {satr}"

    async def ishlovchi(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
        async with self.chegara:
            self.statistika["ulanishlar"] += 1
            try:
                while True:
                    try:
                        async with asyncio.timeout(self.bosh_turish):
                            xom = await reader.readline()
                    except TimeoutError:
                        self.statistika["muddat"] += 1
                        writer.write("XAYR muddat tugadi\n".encode())
                        await writer.drain()
                        break
                    if not xom:
                        break
                    self.statistika["buyruqlar"] += 1
                    writer.write((await self.bajar(xom.decode().strip()) + "\n").encode())
                    await writer.drain()
            finally:
                writer.close()
                await writer.wait_closed()


async def mijoz(port: int, buyruqlar: list[str]) -> list[str]:
    reader, writer = await asyncio.open_connection("127.0.0.1", port)
    javoblar = []
    for buyruq in buyruqlar:
        writer.write(f"{buyruq}\n".encode())
        await writer.drain()
        javoblar.append((await reader.readline()).decode().strip())
    writer.close()
    await writer.wait_closed()
    return javoblar


TASHQI_MIJOZ = """
import socket, sys
s = socket.create_connection(("127.0.0.1", int(sys.argv[1])))
f = s.makefile("rw", encoding="utf-8", newline="\\n")
for b in ["SET tashqi jarayon", "GET tashqi"]:
    f.write(b + "\\n"); f.flush()
    print(f.readline().strip())
"""


async def main() -> None:
    kv = KVServer()
    server = await asyncio.start_server(kv.ishlovchi, "127.0.0.1", 0)
    port = server.sockets[0].getsockname()[1]

    async with server:
        print("=== 1. Bitta mijoz ===")
        javoblar = await mijoz(port, ["SET til python", "GET til", "GET yoq", "DEL til", "GET til", "SALOM"])
        for javob in javoblar:
            print(f"  {javob}")

        print("\n=== 2. 50 ta mijoz bir vaqtda ===")
        vazifalar = [mijoz(port, [f"SET k{i:02d} {i * i}", f"GET k{i:02d}"]) for i in range(50)]
        natijalar = await asyncio.gather(*vazifalar)
        print(f"  hamma SET javobi OK: {all(n[0] == 'OK' for n in natijalar)}")
        print(f"  hamma GET o'z qiymatini oldi: {all(n[1] == str(i * i) for i, n in enumerate(natijalar))}")
        kalitlar = (await mijoz(port, ["KEYS"]))[0].split()
        print(f"  ombordagi kalitlar: {len(kalitlar)}, birinchilari: {kalitlar[:3]}")

        print("\n=== 3. Bo'sh turgan mijoz ===")
        reader, writer = await asyncio.open_connection("127.0.0.1", port)
        xabar = (await reader.readline()).decode().strip()
        print(f"  hech narsa yubormagan mijozga server: {xabar!r}")
        print(f"  keyin ulanish yopildi: {await reader.read() == b''}")
        writer.close()
        await writer.wait_closed()

        print("\n=== 4. Boshqa jarayondan mijoz ===")
        jarayon = await asyncio.create_subprocess_exec(
            sys.executable, "-c", TASHQI_MIJOZ, str(port),
            stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
        )
        chiqish, _ = await asyncio.wait_for(jarayon.communicate(), timeout=20)
        print(f"  tashqi jarayon javoblari: {chiqish.decode().split()}")
        print(f"  chiqish kodi: {jarayon.returncode}")

    print("\n=== 5. Statistika ===")
    print(f"  ulanishlar: {kv.statistika['ulanishlar']}")
    print(f"  buyruqlar:  {kv.statistika['buyruqlar']}")
    print(f"  muddat bo'yicha uzilgan: {kv.statistika['muddat']}")
    print(f"  ⭐ bitta ip, bitta loop — {kv.statistika['ulanishlar']} ta ulanish (1 + 50 + 1 + 1 + 1)")


if __name__ == "__main__":
    asyncio.run(main())

Natijaning muhim qismi:

text
=== 1. Bitta mijoz ===
  OK
  python
  YO'Q
  OK
  YO'Q
  XATO noma'lum buyruq: SALOM

=== 2. 50 ta mijoz bir vaqtda ===
  hamma SET javobi OK: True
  hamma GET o'z qiymatini oldi: True
  ombordagi kalitlar: 50, birinchilari: ['k00', 'k01', 'k02']

=== 3. Bo'sh turgan mijoz ===
  hech narsa yubormagan mijozga server: 'XAYR muddat tugadi'
  keyin ulanish yopildi: True

=== 4. Boshqa jarayondan mijoz ===
  tashqi jarayon javoblari: ['OK', 'jarayon']
  chiqish kodi: 0

=== 5. Statistika ===
  ulanishlar: 54
  buyruqlar:  109
  muddat bo'yicha uzilgan: 1
  ⭐ bitta ip, bitta loop — 54 ta ulanish (1 + 50 + 1 + 1 + 1)

Nima ko'rsatdi: 2.5, 2.6, 2.7-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"asyncio'da poyga holati yo'q" await orasida bor
"asyncio.Queue iplar orasida ishlaydi" Faqat bitta loop ichida
"writer.write ma'lumotni yuboradi" Buferga yozadi; drain kerak
"Asinxron generator break da darhol tozalanadi" Kafolat — aclosing bilan
"threading.Lock asinxron kodda ham ishlaydi" Loopni bloklaydi
"Event.set() bitta vazifani uyg'otadi" Hammasini
"readline() ulanish yopilsa xato beradi" b"" qaytaradi
"Queue.join() ishchilarni to'xtatadi" Faqat task_done larni kutadi

6. Keng tarqalgan xatolar va yechimlari

1. O'qish → await → yozish

python
balans = hisob.balans
await saqla()
hisob.balans = balans + summa          # ❌
async with hisob.qulf: ...             # ✅

2. drain unutilgan

python
writer.write(katta_malumot)            # ⚠️ bufer cheksiz o'sadi
await writer.drain()                   # ✅

3. task_done unutilgan

python
x = await navbat.get()
ishla(x)                               # ❌ join abadiy kutadi
try: ... finally: navbat.task_done()   # ✅

4. Chegarasiz navbat

python
asyncio.Queue()                        # ⚠️ ishlab chiqaruvchi tez bo'lsa xotira to'ladi
asyncio.Queue(maxsize=1000)            # ✅

5. threading.Lock asinxron kodda

python
with threading.Lock(): await ...       # ❌ loop bloklanadi
async with asyncio.Lock(): ...         # ✅

6. Qulf ichida uzoq kutish

python
async with qulf:
    await tarmoq_sorovi()              # ⚠️ hamma kutib qoladi

7. Ulanishni yopmaslik

python
reader, writer = await asyncio.open_connection(...)
# ... writer.close() yo'q            # ❌ resurs sizishi

8. Mijozga muddat bermaslik

python
await reader.readline()                # ⚠️ mijoz jim tursa — abadiy
async with asyncio.timeout(30): ...    # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 9-qism (o'tilgan): iterator va generator protokollari
  • 10-qism (o'tilgan): kontekst menejerlar
  • 14.3-dars (o'tilgan): iplardagi poyga va queue.Queue
  • 14.6-dars (o'tilgan): hodisa sikli
  • 14.8-dars: modelni tanlash
  • 20.1-dars: HTTP qanday ishlaydi — tarmoq ustidagi protokol
  • 20-qism: FastAPI — asinxron endpointlar, fon vazifalari
  • 23-qism: asinxron bazalar (asyncpg, SQLAlchemy async)

8. Eng yaxshi amaliyotlar

  1. Har await ni navbat almashish nuqtasi deb o'qing.

  2. Umumiy holat o'zgarishi await ni kesib o'tsa — asyncio.Lock.

  3. Qulf ichida tarmoq so'rovi qilmang — qulfni qisqa ushlang.

  4. Navbatlarga maxsize bering.

  5. task_done ni finally da chaqiring.

  6. write dan keyin drain, close dan keyin wait_closed.

  7. Har bir o'qishga muddat bering.

  8. Asinxron generatorlar — aclosing bilan.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
import asyncio, contextlib
1.  async def g():
        yield 1
        yield 2
    async def m(): return [x async for x in g()]
    print(asyncio.run(m()))
2.  async def g():
        yield 1
    print(type(g()).__name__)
3.  async def g():
        yield 1
    async def m():
        it = g()
        await anext(it)
        return await anext(it, "tugadi")
    print(asyncio.run(m()))
4.  async def m():
        q = asyncio.Queue()
        await q.put(1); await q.put(2)
        return await q.get(), q.qsize()
    print(asyncio.run(m()))
5.  async def m():
        q = asyncio.LifoQueue()
        for i in range(3): await q.put(i)
        return await q.get()
    print(asyncio.run(m()))
6.  async def m():
        q = asyncio.Queue(maxsize=1)
        await q.put(1)
        return q.full()
    print(asyncio.run(m()))
7.  async def m():
        e = asyncio.Event()
        a = e.is_set(); e.set()
        return a, e.is_set()
    print(asyncio.run(m()))
8.  async def m():
        l = asyncio.Lock()
        async with l:
            return l.locked()
    print(asyncio.run(m()))
9.  async def m():
        s = asyncio.Semaphore(2)
        async with s:
            return s.locked()
    print(asyncio.run(m()))
10. @contextlib.asynccontextmanager
    async def r():
        yield "ok"
    async def m():
        async with r() as x:
            return x
    print(asyncio.run(m()))
11. async def m():
        q = asyncio.PriorityQueue()
        for x in [(2, "b"), (1, "a")]: await q.put(x)
        return (await q.get())[1]
    print(asyncio.run(m()))
12. async def m():
        h = 0
        async def f():
            nonlocal h
            for _ in range(100):
                h += 1
        await asyncio.gather(f(), f())
        return h
    print(asyncio.run(m()))
Javoblar
  1. [1, 2]
  2. async_generator
  3. tugadi — anext ning sukut qiymati
  4. (1, 1)
  5. 2
  6. True
  7. (False, True)
  8. True
  9. False — yana bitta joy bor
  10. ok
  11. a
  12. 200 — await yo'q, poyga yo'q

Vazifa 2: Xatolarni tuzating

python
1.  async def otkazma(hisob, summa):
        balans = await hisob.ol()
        await hisob.saqla(balans - summa)

2.  async def ishchi(navbat):
        while True:
            x = await navbat.get()
            await ishla(x)

3.  async def yubor(writer, bloklar):
        for blok in bloklar:
            writer.write(blok)

4.  qulf = threading.Lock()
    async def yangila():
        with qulf:
            await saqla()

5.  async def ishlovchi(reader, writer):
        while satr := await reader.readline():
            writer.write(javob(satr))
Javoblar
python
1.  async def otkazma(hisob, summa):
        async with hisob.qulf:                  # asyncio.Lock
            balans = await hisob.ol()
            await hisob.saqla(balans - summa)

2.  async def ishchi(navbat):
        while True:
            x = await navbat.get()
            try:
                await ishla(x)
            finally:
                navbat.task_done()              # join ishlashi uchun

3.  async def yubor(writer, bloklar):
        for blok in bloklar:
            writer.write(blok)
            await writer.drain()                # orqa bosim

4.  qulf = asyncio.Lock()
    async def yangila():
        async with qulf:
            await saqla()

5.  async def ishlovchi(reader, writer):
        try:
            while True:
                async with asyncio.timeout(30):
                    satr = await reader.readline()
                if not satr:
                    break
                writer.write(javob(satr))
                await writer.drain()
        finally:
            writer.close()
            await writer.wait_closed()

Vazifa 3: Chat serveri

asyncio stream lari bilan chat serveri yozing:

  1. Har mijoz NICK ism bilan kiradi
  2. Yozilgan xabar boshqa barcha mijozlarga tarqatiladi
  3. Har mijoz uchun alohida Queue(maxsize=100) — sekin mijoz boshqalarni to'xtatmasin
  4. Navbati to'lgan mijoz uziladi
  5. 100 ta sun'iy mijoz bilan sinab, hamma xabar yetib borganini tekshiring

Vazifa 4: Asinxron veb-o'rgimchak

  1. Boshlang'ich URL lardan asyncio.Queue orqali sahifalarni aylanib chiqing (sun'iy "tarmoq" funksiyasi bilan)
  2. Bir vaqtda ko'pi bilan 10 so'rov (Semaphore)
  3. Ko'rilgan URL lar to'plami — takrorlanmasin
  4. Chuqurlik chegarasi
  5. Hamma ishchi bo'sh qolganda to'xtash (join + to'xtatish signali)

Vazifa 5: Tezlik cheklovchi (rate limiter)

TezlikChegarasi(soniyada=10) asinxron kontekst menejerini yozing:

  1. "Token paqiri" (token bucket) algoritmi
  2. async with chegara: — token bo'lmasa kutadi
  3. 100 ta vazifa bilan sinab, soniyasiga 10 tadan oshmasligini isbotlang
  4. Condition yoki Event asosida adolatli navbat qiling

Vazifa 6: Parallel buyruqlar ishga tushiruvchisi

  1. Buyruqlar ro'yxatini create_subprocess_exec bilan parallel bajaring
  2. Bir vaqtda ko'pi bilan 4 ta
  3. Har buyruqqa muddat; oshsa — kill()
  4. Chiqishni satrma-satr, buyruq nomi bilan jonli chop eting
  5. Oxirida jadval: buyruq, kod, vaqt

Vazifa 7: O'ylash

Iplar bilan yozilgan kodda poyga holati istalgan ikki bayt-kod ko'rsatmasi orasida yuz berishi mumkin (14.3-dars). asyncio'da esa — faqat await da. Bu farq kodni tekshirish (code review), testlash va nosozlik izlashga qanday ta'sir qiladi? asyncio'ning bu afzalligini nima buzishi mumkin?

Javob

Qisqa javob: asyncio'da xavfli nuqtalar ko'rinadigan va aniq — faqat await, async for, async with. Bu kod tekshirish va nosozlik izlashni ancha osonlashtiradi, poygalarni esa takrorlanadigan qiladi. Lekin bu afzallikni iplar, to_thread, yashirin await li yordamchi funksiyalar va tashqi holat (baza, fayl) buzishi mumkin.

1. Kodni tekshirish

Iplar asyncio
Xavfli nuqtalar Har qatorda Faqat await da
Tekshiruvchi nimaga qaraydi Hamma umumiy holatga await ni kesib o'tadigan o'qish/yozishga
Qoida "Umumiy o'zgaruvchi — qulf" "O'qish → await → yozish — qulf"

Tekshiruvchi await lar orasidagi blokni atomar tranzaksiya deb o'qiy oladi.

2. Testlash va takrorlanuvchanlik

  • Iplarda poyga natijasi har ishga tushirishda boshqacha (14.3-dars) — test ba'zan o'tadi, ba'zan yiqiladi
  • asyncio'da rejalashtirish deterministik: bu darsdagi Misol 3 har safar aynan 1000 beradi
  • Demak poygani testda qayta hosil qilish va tuzatilganini isbotlash mumkin
  • await asyncio.sleep(0) ni ataylab qo'yib, eng yomon navbatni taqlid qilish mumkin

3. Nosozlik izlash

  • Stek izi (traceback) bitta ipda — tushunarli
  • asyncio.all_tasks() va task.print_stack() bilan hamma vazifa holatini ko'rish mumkin
  • debug=True sekin callback larni ko'rsatadi

4. Afzallikni nima buzadi

Buzuvchi Nega
asyncio.to_thread / run_in_executor Funksiya boshqa ipda — umumiy holatga tegsa, iplar poygasi qaytadi
Yashirin await await yordamchi() ichida yana await — "atomar" deb o'ylangan blok atomar emas
Tashqi holat Baza, fayl, kesh — boshqa jarayonlar ham o'zgartiradi; Lock yordam bermaydi
Bir nechta loop / jarayon asyncio.Lock faqat bitta loop ichida himoya qiladi
Free-threading (3.13t+) Loop va to_thread ishchilari haqiqiy parallel ishlaydi
Callback lar (call_soon) Navbat tartibi o'zgarishi mumkin

5. Amaliy qoidalar

  1. await ni ko'rganda: "bu yerda dunyo o'zgarishi mumkin" deb o'ylang
  2. to_thread ga faqat o'z ma'lumotini oladigan sof funksiyalarni bering
  3. Tashqi holat uchun — baza tranzaksiyalari, optimistik qulflash (versiya ustuni)
  4. Ko'p jarayonli xizmatlarda — taqsimlangan qulf (Redis, Postgres advisory lock — 29-qism)

6. Xulosa

  1. asyncio poygani yo'q qilmaydi — uni ko'rinadigan va takrorlanadigan qiladi
  2. Bu kod tekshirish, testlash va nosozlik izlashni osonlashtiradi
  3. Iplarga chiqish, yashirin await va tashqi holat bu afzallikni buzadi
  4. Qoida bitta: await ni kesib o'tadigan o'qish-yozish — himoya talab qiladi

Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.


Xulosa

Bu darsda asinxron dasturning asosiy g'ishtlarini amaliyotda qo'lladik.

Eng muhim uch fikr:

  1. Asinxron protokollar sinxronlarning aynan nusxasi. async for / __anext__ / StopAsyncIteration — iteratsiya, async with / __aenter__ / __aexit__ — resurslar, @asynccontextmanager — qisqa kontekst menejerlar. Asinxron generatorni o'rtada tashlab ketsangiz, tozalashni contextlib.aclosing kafolatlaydi.

  2. asyncio.Queue — quvurlarning asosi. put/get/task_done/join ishlab chiqaruvchi va iste'molchilarni bog'laydi, maxsize esa orqa bosim beradi: tez ishlab chiqaruvchi sekin iste'molchini kutadi va xotira o'smaydi. Ishchilarni to'xtatish uchun alohida signal (masalan, None) kerak — join ularni to'xtatmaydi.

  3. Bitta ip poygadan himoya qilmaydi. O'qish → await → yozish ketma-ketligida boshqa vazifa holatni o'zgartirib ulguradi: misolda 5000 ta yozuvdan 4000 tasi yo'qoldi. Yechim — asyncio.Lock (threading.Lock emas). Afzalligi: xavfli joylar faqat await larda va natija takrorlanadi. Tarmoqda esa stream lar: write → drain, close → wait_closed va har o'qishga muddat.

Keyingi darsda 14-qismni yakunlaymiz: iplar, jarayonlar, subinterpretatorlar va asyncio — qaysi vazifaga qaysi modelni tanlash kerak, o'lchovlar bilan.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
14.7-dars: async / await amaliyoti — IlmHamroh