---
title: 'Eşzamansız Kütüphane Uyumu'
source: 'https://academia.sh/tr/kurslar/python-eszamanlilik/eszamansiz-kutuphane-uyumu'
course: 'Eşzamanlılık ve Başarım'
language: tr
updated: '2026-08-17T18:10:23+00:00'
license: 'CC BY-SA 4.0'
---

# Eşzamansız Kütüphane Uyumu

Her görevde tek bir engelleyen çağrı örtüşen adımı 44'ten 37'ye düşürür, sekiz görevin yedisi engellediğinde örtüşme 6 teslime rağmen 0'a iner; çağrıyı yürütücü havuzuna devretmek örtüşmeyi 44'e döndürür ama bedelin görev başına mı çağrı başına mı düştüğü tik sayısını 44 ile 90 arasında ayırır.

Önceki dersin tablosunda üç satır vardı ve üçüncüsü şuydu: denetimi hiç bırakmayan eş yordam
**0 teslim, 80 tik, 0 örtüşen adım**. `async def` ile yazılmış, gerçek bir eş yordamdı ve
sonucu tek akıştan ayırt edilemiyordu.

O satır bir yazım hatası gibi görünür ama pratikte hiç öyle çıkmaz. Gövdeyi yazan kişi
genellikle `await` yazmayı unutmaz; unutan **çağırdığı işlevdir**. Denetimi bırakmayı bilmeyen
bir çağrı, onu çağıran eş yordamın da bırakmasını engeller. Bu dersin ölçtüğü şey o çağrının
faturasıdır: akıştaki bir çağrı denetimi bırakmadığında örtüşen adım kaç düşer, bütün çağrılar
bırakmadığında ne kalır, ve bırakmayı bilmeyen bir çağrıyı akışa geri sokmanın bedeli kaç
adımdır?

## Engelleyen Kod Teslim Noktası Üretmeyen Koddur

**Engelleyen kod (blocking code)**, denetimi olay döngüsüne geri vermeden işini bitiren
koddur. Adı yanıltıcı olabilir: engellenen şey çağıran gövde değil, **döngünün kendisidir.**
Gövde zaten sonucu bekleyecekti; asıl kayıp, o beklerken başka hiçbir görevin ilerleyememesi.

Bir çağrının engelleyen olup olmadığı ne yaptığından değil, **nasıl yazıldığından** gelir.
Aynı iş — bir dosyayı okumak, bir yanıtı beklemek — teslim noktası üreten bir biçimde de
yazılabilir, üretmeyen bir biçimde de. Eşzamansız bir akışta ikisi arasındaki fark, önceki
dersin tablosunda ölçülen farkın kendisidir.

Buradan bu dersin dar sorusu doğuyor. Bir programda çağrıların hepsi birden değişmez; kimi
teslim eder, kimi etmez. Karışım hâlinde ne çıkar?

## Bir Çağrı Yeri, Sekiz Görev

Ölçüm iki ekseni ayırıyor, çünkü ikisi karıştırıldığında sonuç okunamaz hâle geliyor.

**Birinci eksen çağrı sayısı:** her görevin akışındaki kaç girdi/çıktı çağrısının engellediği.
Bir görevin akışında on adım var ve bunların bir kısmı girdi/çıktı adımı; bu adımların kaçının
teslim etmeyen bir çağrıdan geçtiği ölçülüyor. Burada dikkat edilecek nokta şu: kodda **tek
bir çağrı yeri** engelleyen olabilir ama o çağrı yerinden sekiz görev de geçer. Bir satırlık
kusur, sekiz akışa birden yazılır.

**İkinci eksen görev sayısı:** kaç görevin baştan sona engelleyen çağrılar kullandığı. Bir
programın bir bölümü eşzamansız yazılmışken bir bölümünün eski biçimde kalması bu eksende
ölçülür.

Ölçümün varsayımları:

- **EM48** — Görev kurgusu önceki derslerin aynısıdır: sekiz görev, görev başına on adım,
  toplam seksen adım, girdi/çıktı ağırlıklı yük, tek yuva.
- **EM49** — Eş yordamlar ve onları sürdüren döngü önceki dersin aynısıdır; değişen tek şey
  gövdenin hangi adımda teslim ettiğidir.
- **EM50** — Uyumlu çağrı, girdi/çıktı adımında denetimi bırakan çağrıdır. Engelleyen çağrı,
  aynı adımı denetimi bırakmadan atan çağrıdır. **İkisi de aynı işi yapar ve aynı adımı
  harcar.**
- **EM51** — İşlemci adımları hiçbir rejimde teslim etmez; ölçülen değişken yalnız girdi/çıktı
  çağrılarının biçimidir.
- **EM52** — Birinci eksende her görevin **ilk** `k` girdi/çıktı çağrısı engeller; hangi
  çağrıların seçildiği kurguya yazılıdır ve koşumlar arasında değişmez.
- **EM53** — İkinci eksende ilk `k` görev baştan sona engeller, kalanı uyumludur.
- **EM54** — Yürütücü havuzuna devir modelde **eklenen bir işlemci adımıdır**: iş havuzda
  koşar, döngü teslim noktasını geri kazanır, karşılığında bir adım daha sayılır.
- **EM55** — Devir bedelinin iki yerleşimi ölçülür: görev başına bir kez ve çağrı başına bir
  kez. İkisi de aynı işi devreder, yalnız bedelin düştüğü yer değişir.
- **EM56** — Havuzun genişliği bu ölçümde sınırsızdır; havuz genişliğinin etkisi sonraki
  dersin konusudur.
- **EM57** — Süre ölçülmez; sayılan birim adım, tik, örtüşen adım ve teslimdir.
- **EM58** — Ölçüm tek koşumdur ve tohum sabittir.

## Ölçüm

```python
"""Engelleyen kod: teslim etmeyen bir cagri ortusmeyi ne kadar goturur."""

TOHUM = 20260817
ISLEMCI, GC = "islemci", "g_c"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gorevler(sayi=8, adim=10, gc_payi=7, tohum=TOHUM):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    r, liste = uretec(tohum), []
    for i in range(sayi):
        liste.append([GC if r(10) < gc_payi else ISLEMCI for _ in range(adim)])
    return liste


class Bekleyis:
    """Es yordamin denetimi olay dongusune biraktigi nokta."""

    def __init__(self, tur):
        self.tur = tur

    def __await__(self):
        return (yield self.tur)


def uyumlu_yapici(engelleyen_cagri=0):
    """Her gorevin ilk 'engelleyen_cagri' g/c cagrisi teslim etmez."""
    async def gorev(adimlar, gunluk):
        kalan = engelleyen_cagri
        for a in adimlar:
            if a == GC and kalan > 0:
                kalan -= 1              # engelleyen cagri: denetim birakilmaz
            elif a == GC:
                await Bekleyis(a)       # uyumlu cagri: denetim birakilir
            gunluk.append(a)
    return gorev


def karma_yapici(engelleyen_gorev=0):
    """Ilk 'engelleyen_gorev' gorev hic teslim etmez, kalani uyumludur."""
    uyumlu = uyumlu_yapici(0)

    def yapici(adimlar, gunluk):
        yapici.sira += 1
        if yapici.sira <= engelleyen_gorev:
            return uyumlu_yapici(len(adimlar))(adimlar, gunluk)
        return uyumlu(adimlar, gunluk)
    yapici.sira = 0
    return yapici


def dongu(yapici, gorevler, islemci_yuvasi=1):
    """El yazimi belirlenimci olay dongusu. Donen: tik, ortusen, teslim."""
    gunluk = [[] for _ in gorevler]
    coro = [yapici(g, gunluk[i]) for i, g in enumerate(gorevler)]
    istek = [None] * len(coro)
    canli = [True] * len(coro)
    borc = [0] * len(coro)
    teslim = 0

    def surdur(i, pesin=0):
        nonlocal teslim
        onceki = len(gunluk[i])
        try:
            istek[i] = coro[i].send(None)
            teslim += 1
        except StopIteration:
            istek[i] = None
            canli[i] = False
        borc[i] += max(0, len(gunluk[i]) - onceki - pesin)

    for i in range(len(coro)):
        surdur(i)
    tik = ortusen = 0
    while any(canli) or any(borc):
        mesgul = [i for i in range(len(coro)) if borc[i] > 0]
        if mesgul:
            borc[mesgul[0]] -= 1
            tik += 1
            continue
        yuva, ilerleyen = islemci_yuvasi, 0
        for i in range(len(coro)):
            if not canli[i] or istek[i] is None:
                continue
            if istek[i] == ISLEMCI:
                if yuva <= 0:
                    continue
                yuva -= 1
            surdur(i, 1)
            ilerleyen += 1
        if ilerleyen == 0:
            break
        tik += 1
        ortusen += max(0, ilerleyen - 1)
    return tik, ortusen, teslim


g = gorevler(gc_payi=7)
print("sekiz görev, seksen adım, girdi/çıktı ağırlıklı yük, tek yuva")
print()
print("A — her görevde kaç girdi/çıktı çağrısı engelliyor")
print(f"{'engelleyen çağrı':>17s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for k in range(9):
    tik, ortusen, teslim = dongu(uyumlu_yapici(k), g)
    print(f"{k:17d} {teslim:7d} {tik:5d} {ortusen:8d}")

print()
print("B — kaç görev baştan sona engelliyor")
print(f"{'engelleyen görev':>17s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for k in range(9):
    tik, ortusen, teslim = dongu(karma_yapici(k), g)
    print(f"{k:17d} {teslim:7d} {tik:5d} {ortusen:8d}")

print()
print("C — engelleyen çağrıyı yürütücü havuzuna devretmek")
devir_gorev = [[ISLEMCI] + list(t) for t in g]
devir_cagri = [[s for a in t for s in ([ISLEMCI, GC] if a == GC else [a])]
               for t in g]
print(f"{'rejim':<28s} {'adım':>5s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for ad, dizi, yapici in (
        ("hepsi engelliyor", g, uyumlu_yapici(10)),
        ("hepsi uyumlu", g, uyumlu_yapici(0)),
        ("devir görev başına bir kez", devir_gorev, uyumlu_yapici(0)),
        ("devir çağrı başına", devir_cagri, uyumlu_yapici(0))):
    tik, ortusen, teslim = dongu(yapici, dizi)
    print(f"{ad:<28s} {sum(len(t) for t in dizi):5d} {teslim:7d} "
          f"{tik:5d} {ortusen:8d}")
```

```
sekiz görev, seksen adım, girdi/çıktı ağırlıklı yük, tek yuva

A — her görevde kaç girdi/çıktı çağrısı engelliyor
 engelleyen çağrı  teslim   tik  örtüşen
                0      54    36       44
                1      46    43       37
                2      38    50       30
                3      30    57       23
                4      22    64       16
                5      14    71        9
                6       8    76        4
                7       5    78        2
                8       2    80        0

B — kaç görev baştan sona engelliyor
 engelleyen görev  teslim   tik  örtüşen
                0      54    36       44
                1      46    44       36
                2      40    50       30
                3      30    58       22
                4      22    64       16
                5      17    69       11
                6      11    75        5
                7       6    80        0
                8       0    80        0

C — engelleyen çağrıyı yürütücü havuzuna devretmek
rejim                         adım  teslim   tik  örtüşen
hepsi engelliyor                80       0    80        0
hepsi uyumlu                    80      54    36       44
devir görev başına bir kez      88      54    44       44
devir çağrı başına             134      54    90       44
```

## Tek Bir Çağrının Bedeli

A tablosunun ilk iki satırı bu dersin en kısa sonucunu veriyor. Bütün çağrılar uyumluyken
**36 tik / 44 örtüşen adım**. Her görevde **tek bir** girdi/çıktı çağrısı engellediğinde
**43 tik / 37 örtüşen adım**.

Tek çağrı, yedi tik ve yedi örtüşen adım götürüyor. Kodda değişen şey bir satır; ölçümde
değişen şey örtüşmenin altıda biri.

Kayıp oranı, engelleyen çağrının payından **büyük**. Elli dört girdi/çıktı çağrısının sekizi
engelliyor, yani çağrıların yaklaşık yedide biri; örtüşen adımdaki kayıp ise 44'ün 7'si.
Sebebi engelleyen çağrının kendi görevini değil, **o tikte ilerleyebilecek herkesi**
durdurması. Bir görev engellerken öbür yedisi hazır bekliyor olabilir; ilerlemiyorlar.

Tablonun kalanı bu eğrinin sonuna kadar gidiyor: iki çağrıda **30**, dört çağrıda **16**,
altı çağrıda **4** örtüşen adım. Sekiz çağrıda — yani her görevin bütün girdi/çıktı çağrıları
engellediğinde — örtüşen adım **0** ve tik **80**. Rejim tek akışa dönüyor.

Bu son satır, dersin başlığındaki uyumun ne demek olduğunu tanımlıyor: eşzamansız bir akışın
sayısı, akıştaki **en engelleyen** parçaya göre belirleniyor.

## Yedi Görev Yeter

B tablosu aynı çöküşü öbür eksende gösteriyor ve bir satırı ötekilerden ayrılıyor.

**Yedi görev** engellediğinde tik **80**, örtüşen adım **0**. Sekizinci görev hâlâ uyumlu;
altı kez denetim bırakıyor. Teslim sütunu bunu yazıyor: **6 teslim, 0 örtüşen adım.**

Önceki dersin kuralı burada tam olarak sınanıyor. Teslim örtüşme için gerekliydi ama yeterli
değildi; ikinci bir hazır görev de gerekiyordu. Yedi görev kendi engelleyen çağrılarında
takılıyken sekizinci görev denetimi bırakıyor ve bırakacağı denetimi alacak kimse bulunmuyor.
Döngü aynı görevi geri sürdürüyor.

Bunun pratik okuması sert: **eşzamansız bir akışı bir tek uyumlu parça kurtarmaz.** Bir kod
tabanının bir bölümünü eşzamansız yazmak, kalan bölüm engellediği sürece ölçülebilir bir şey
kazandırmayabilir. B tablosunun ilk yarısı da aynı yönü gösteriyor: bir görev engellediğinde
kayıp **8** örtüşen adım, ikisinde **14**, üçünde **22**. Kayıp görev sayısıyla birlikte
hızlanarak büyüyor, çünkü her engelleyen görev hem kendi örtüşmesini hem başkalarının
örtüşme fırsatını götürüyor.

## Engelleyenin Görünmezliği

İki tablonun ortak bir özelliği var ve kusurun neden bu kadar sık kaçtığını açıklıyor: hiçbir
satırda **hata yok**. Sekiz görevin sekizi de bütün adımlarını atıyor, hiçbiri yarım kalmıyor,
hiçbir istisna çıkmıyor. Değişen tek şey adımların hangi tike düştüğü.

Çağrı yerine bakarak da anlaşılmıyor. Uyumlu çağrı ile engelleyen çağrı ölçümde aynı işi
yapıyor ve aynı adımı harcıyor; ayrıldıkları yer gövdenin içinde. Bir işlevin engelleyen olup
olmadığını söyleyen tek şey, çağrıldığında sürdürülebilir bir nesne verip vermediğidir —
`async def` ile tanımlanmış bir işlev bir eş yordam nesnesi döndürür, sıradan bir işlev
doğrudan sonucu.

Bunun doğrudan sonucu şu: eşzamansız bir akışta **`await` yazılmayan her çağrı adaydır.**
Sonucu doğrudan dönen bir çağrı ya hiç beklemiyor ya da bekleyip döngüyü tutuyordur; ikisi
çağrı yerinden ayırt edilemez.

Ölçümün buradaki katkısı, kusurun **belirtisini** adlandırmak. Engelleyen bir çağrı hata
üretmez, çıktıyı bozmaz, günlüğe bir şey yazmaz. Ürettiği tek belirti örtüşen adımın
düşmesidir — ve örtüşen adım sayılmıyorsa hiçbir belirti kalmaz. B tablosunun yedinci satırı
bunun en uç hâli: program eşzamansız yazılmış, altı teslim noktası gerçekten çalışıyor ve
sonuç tek akışla aynı.

## Havuza Devir ve Bedelin Düştüğü Yer

C tablosu düzeltmeyi ölçüyor. Engelleyen çağrının uyumlu bir karşılığı yoksa, çağrı olduğu
gibi bırakılıp **başka bir yerde** koşturulabilir: bir **yürütücü havuzu**na devredilir, olay
döngüsü o sırada teslim noktasını geri kazanır.

İki uçtaki satırlar bilinen sayılar: hepsi engelliyorsa **80 adım, 0 teslim, 80 tik, 0 örtüşen
adım**; hepsi uyumluysa **80 adım, 54 teslim, 36 tik, 44 örtüşen adım**.

Devir iki satır ekliyor ve ikisinin de örtüşen adımı **44** — devir örtüşmeyi tam olarak geri
kazandırıyor. Ayrıldıkları yer tik sayısı.

**Devir bedeli görev başına bir kez** düştüğünde toplam adım 80'den **88**'e çıkıyor ve tik
**44** oluyor. Engelleyen rejimin **80** tikine göre yarı yarıya kazanç, uyumlu rejimin
**36** tikine göre sekiz tik geride. Bedel sekiz adım, kayıp sekiz tik; hesap birebir.

**Devir bedeli çağrı başına** düştüğünde toplam adım **134**'e çıkıyor ve tik **90** oluyor.
Bu satır dersin en dikkat çekici sayısı: **90, hiçbir şey yapmayıp engellemenin 80 tikinden
büyük.** Örtüşen adım 0'dan 44'e çıkmış olmasına rağmen toplam kötüleşmiş.

Çelişki değil, aritmetik. Devredilen her çağrı bir adım ekliyor ve elli dört çağrı elli dört
adım ediyor; kazanılan kırk dört örtüşen adım bu ekten küçük. Örtüşen adımı tek başına okumak
bu yüzden yanıltır — üçüncü dersin aktarım tablosunda da aynı tuzak vardı. **Bir ölçüm, hangi
adımların sayıya girdiğini söylemeden karşılaştırılamaz.**

Buradan çıkan yönerge somut: devri **çağrı başına** değil, mümkün olan en büyük parça başına
yapmak. Sekiz görevi tek tek devretmek sekiz adım, her çağrıyı ayrı devretmek elli dört adım
ediyor. Devredilecek işin büyüklüğü, devrin kazanıp kazanmayacağını doğrudan belirliyor.

Üç satırın birlikte verdiği sıra da bir öncelik kuralı öneriyor. En iyi sonuç uyumlu çağrıya
geçmek (**36** tik), ikinci en iyi büyük parçalar hâlinde devretmek (**44**), en kötüsü ise
her çağrıyı ayrı devretmek (**90**). Havuz, uyumlu çağrının yerini tutan bir çözüm değil,
uyumlu karşılığı bulunmadığında başvurulan bir çözümdür ve bedeli her zaman sayılabilir.

## Özet

- Engelleyen kod, denetimi olay döngüsüne geri vermeyen koddur; engellediği şey çağıran gövde
  değil **döngünün kendisidir**.
- Her görevde tek bir engelleyen girdi/çıktı çağrısı, örtüşen adımı **44**'ten **37**'ye
  düşürür ve tik'i **36**'dan **43**'e çıkarır; bütün çağrılar engellediğinde sonuç **80 tik /
  0 örtüşen adım**, yani tek akıştır.
- Sekiz görevin **yedisi** engellediğinde örtüşen adım **0**'dır; sekizinci görev **6** kez
  denetim bıraksa da bırakılan denetimi alacak hazır görev yoktur.
- Çağrıyı yürütücü havuzuna devretmek örtüşen adımı **44**'e geri getirir; devir bedeli görev
  başına düştüğünde tik **44**, çağrı başına düştüğünde **90** olur.
- Çağrı başına devir, hiç düzeltmemekten daha çok tik üretir (**90**'a karşı **80**): kazanılan
  örtüşme, eklenen adım sayısından küçük kaldığında ölçüm ters işaret verir.

## Sonraki Adım

Bu dersin son bölümü havuzu bir düzeltme aracı olarak kullandı ve genişliğini hiç sormadı;
havuz sınırsız sayıldı. Gerçek bir havuzun genişliği vardır ve arkasında ne olduğu — iş
parçacığı mı, süreç mi — ölçümü değiştirir. Sonraki ders bunu sorar: aynı arayüzle iş
dağıtmak, arkasında iş parçacığı varken ve süreç varken **aynı görevde** hangi sayıları verir,
ve arayüzün aynı olması seçimin de aynı olduğu anlamına gelir mi?
