---
title: 'Eşzamansız Girdi/Çıktı'
source: 'https://academia.sh/tr/kurslar/rust-eszamanlilik/eszamansiz-girdi-cikti'
course: 'Eşzamanlılık ve Asenkron Rust'
language: tr
updated: '2026-09-13T05:09:28+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Girdi çıktı beklerken duraklamanın, hazır olmayan bir kaynağı modelleyen bir gelecekle nasıl kurulduğunu gösterir'
    - 'Farklı hazır olma gecikmeleri taşıyan kaynakların yoklama sayılarını karşılaştırarak bu sayının kaynağın kendi mantığından geldiğini doğrular'
    - 'Standart Kütüphane ve Ağ Uygulamaları kursunun tip bilgisi üzerinden ölçtüğü sınırla bu dersin ilerleme koşulu üzerinden ölçtüğü sınır arasındaki farkı açıklar'
---

# Eşzamansız Girdi/Çıktı

Girdi çıktı beklerken duraklama, gerçek bir ağ ya da dosya değil, hazır olmayınca Pending dönen bir kaynakla modellenir; ölçülen kaç yoklamada tamamlandığı, kaynağın hazır olma süresi değil.

Önceki ders iki görev türünü inceledi: hiç duraklamayan ve kendi kararıyla bir kez duraklayan.
İkisinde de duraklamanın nedeni görevin kendi mantığıydı — dışarıdan hiçbir koşula bağlı
değildi. Bir programın gerçek dünyada beklediği şeylerin çoğu böyle değil: bir ağ bağlantısı
cevap vermeden önce belirsiz bir süre geçer, bir dosya sistemine gönderilen okuma isteği
donanımın hazır olmasını bekler. Bu ders, bu tür bir bekleyişi ele alıyor — ama kursun ortama
bağlı veri yasağı gereği gerçek bir ağ ya da dosya kullanmadan. Bekleme, hazır olmayan bir
kaynağın `Pending` döndüğü, hazır olduğunda kendini uyandırdığı bir modelle kuruluyor.

Gerçek bir ağ soketi ya da dosya tanıtıcısı kullanmamanın nedeni yalnız kolaylık değil.
Kursun başından beri sürdürdüğü belirlenimcilik kısıtı, koşumun hangi makinede çalıştırılırsa
çalıştırılsın aynı sayıyı vermesini istiyor. Gerçek bir ağ isteğinin ne kadar süreceği ağın
kendisine, gerçek bir dosya okumasının ne kadar süreceği depolama aygıtına bağlı — bu
kursun beş ayrı koşumda birebir aynı çıktı vermesini imkânsız kılardı. Bunun yerine,
gerçek dünyadaki "kaynak henüz hazır değil" durumunun **soyut biçimi** yakalanıyor: bir
`poll` çağrısının `Pending` dönmesi ve daha sonra bir `Ready`ye dönüşmesi. Kaynağın neden
hazır olmadığı — bir ağ gecikmesi mi, bir disk erişimi mi — bu modelin dışında kalıyor;
modelin içinde kalan tek şey, hazır olana kadar kaç kez sorulacağı.

## Hazır Olmayan Bir Kaynağı Modellemek

`SahteKaynak`, bir girdi/çıktı kaynağının en indirgenmiş hâli: kaç yoklama sonra hazır
olacağını bir sayaçla taşıyor. Her `poll` çağrısında sayaç sıfır değilse bir azalıyor,
`Pending` dönüyor ve kendini uyandırıyor; sıfıra ulaştığında kanala bir kayıt yazıp `Ready`
dönüyor.

```rust
// ilerle.rs — cekirdegin kesiti: hazir olmayan bir kaynagi modelleyen gelecek.

use std::future::Future;
use std::pin::Pin;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, Wake, Waker};

pub struct Uyandirici {
    pub hazir: Mutex<bool>,
    pub kosul: Condvar,
}

impl Wake for Uyandirici {
    fn wake(self: Arc<Self>) {
        *self.hazir.lock().unwrap() = true;
        self.kosul.notify_one();
    }
}

pub fn kostur<F: Future>(gorev: F) -> (F::Output, u32) {
    let mut gorev = Box::pin(gorev);
    let u = Arc::new(Uyandirici {
        hazir: Mutex::new(false),
        kosul: Condvar::new(),
    });
    let waker: Waker = u.clone().into();
    let mut baglam = Context::from_waker(&waker);
    let mut yoklama = 0;
    loop {
        yoklama += 1;
        match gorev.as_mut().poll(&mut baglam) {
            Poll::Ready(x) => return (x, yoklama),
            Poll::Pending => {
                let mut h = u.hazir.lock().unwrap();
                while !*h {
                    h = u.kosul.wait(h).unwrap();
                }
                *h = false;
            }
        }
    }
}

// hazir olmayan bir girdi/cikti kaynagini modelliyor: her yoklamada bir yoklama kalan
// sayisini azaltiyor, kalan sifira dusunce hazir kabul edip degeri gonderiyor.
pub struct SahteKaynak {
    pub kalan: u32,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

impl Future for SahteKaynak {
    type Output = &'static str;
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<&'static str> {
        if self.kalan == 0 {
            self.kanal.send(self.ad.to_string()).unwrap();
            Poll::Ready(self.ad)
        } else {
            self.kalan -= 1;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }
}

pub async fn veriyi_oku(kaynak: SahteKaynak, kanal: Sender<String>) -> &'static str {
    let _ = kaynak.await;
    kanal.send(String::from("okuma-tamamlandi")).unwrap();
    "okundu"
}
```

`SahteKaynak`, önceki dersteki `BirKezDuraklar`ın genellenmiş biçimi: `BirKezDuraklar` tam
olarak bir kez duraklıyordu, `SahteKaynak` `kalan` alanında yazan sayı kadar duraklıyor. Bu
sayı gerçek bir programda bir donanım kesmesinin, bir işletim sistemi bildiriminin ya da bir
ağ paketinin gelişinin karşılığı olurdu; burada yalnız bir tam sayı ve kaynağın **davranışını**
belirliyor — davranışını temsil ettiği gerçek olayı değil. `veriyi_oku`, bu kaynağı bekleyen
bir `async fn`: `kaynak.await` bitene kadar kendi de ilerlemiyor, tıpkı önceki derste içteki
bir geleceği bekleyen dış gövdenin yaptığı gibi.

`SahteKaynak`in `poll` yöntemi her `Pending` dönüşünde `cx.waker().wake_by_ref()` çağırıyor —
tıpkı `BirKezDuraklar` gibi. Bu, gerçek bir kaynakla arasındaki en önemli fark: gerçek bir ağ
soketi kendi kendini uyandırmaz, işletim sisteminin bir bildirim mekanizması (soket hazır
olduğunda çalışma zamanına haber veren bir alt sistem) `Waker`i çağırır. `SahteKaynak` bu
dış bildirimi taklit ediyor — kendi zamanlayıcısı olmadığı için, kendini her seferinde hemen
uyandırıp bir sonraki `poll` çağrısında `kalan`ı bir azaltmayı tercih ediyor. Sonuç, dışarıdan
bakıldığında aynı: yürütücü `Pending` görüyor, bir süre sonra tekrar yoklama fırsatı buluyor,
kaynak sonunda hazır oluyor.

## Kaç Yoklama, Ne Kadar Bekleme

Aşağıdaki program üç farklı gecikmeyle kurulmuş kaynakları koşturuyor: biri kısa (bir yoklama
bekliyor), biri uzun (dört yoklama bekliyor), biri de bir `async fn` içinden bekleniyor.

```rust
// girdi-cikti.rs — hazir olmayan bir kaynagin kac yoklamada tamamlandigi.
mod ilerle;
use ilerle::{kostur, veriyi_oku, SahteKaynak};
use std::sync::mpsc::channel;

fn main() {
    let (gonder, al) = channel::<String>();

    let (_deger_a, yoklama_a) = kostur(SahteKaynak {
        kalan: 1,
        ad: "kaynak-kisa-bekleme",
        kanal: gonder.clone(),
    });
    let (_deger_b, yoklama_b) = kostur(SahteKaynak {
        kalan: 4,
        ad: "kaynak-uzun-bekleme",
        kanal: gonder.clone(),
    });

    let kaynak_c = SahteKaynak {
        kalan: 2,
        ad: "kaynak-ic",
        kanal: gonder.clone(),
    };
    let (_deger_c, yoklama_c) = kostur(veriyi_oku(kaynak_c, gonder.clone()));

    drop(gonder);
    let mut kayitlar: Vec<String> = al.try_iter().collect();
    kayitlar.sort();

    println!("kisa bekleme  (kalan=1) -> {} yoklamada tamamlandi", yoklama_a);
    println!("uzun bekleme  (kalan=4) -> {} yoklamada tamamlandi", yoklama_b);
    println!("okuma, icteki kaynagi bekliyor -> {} yoklamada tamamlandi", yoklama_c);
    println!();
    println!("kayitlar ({}):", kayitlar.len());
    for k in &kayitlar {
        println!("  {}", k);
    }
}
```

```
kisa bekleme  (kalan=1) -> 2 yoklamada tamamlandi
uzun bekleme  (kalan=4) -> 5 yoklamada tamamlandi
okuma, icteki kaynagi bekliyor -> 3 yoklamada tamamlandi

kayitlar (4):
  kaynak-ic
  kaynak-kisa-bekleme
  kaynak-uzun-bekleme
  okuma-tamamlandi
```

**AS5.** Yoklama sayısı, `kalan` alanının başlangıç değerinden bire fazla: `kalan: 1` iki
yoklamada, `kalan: 4` beş yoklamada tamamlanıyor. Kural, Async/Await dersindeki $n+1$ kuralıyla
aynı aileden — orada $n$ ardışık duraklama noktası vardı, burada $n$ ardışık `Pending` dönüşü
var; ikisi de son çağrıyı, `Ready` dönen çağrıyı, sayıya dahil ediyor. `kaynak-ic` (`kalan:
2`), `veriyi_oku` içinden bekleniyor ve üç yoklamada tamamlanıyor — iki `Pending` artı bir
`Ready`, tıpkı doğrudan `kostur`a verilseydi tamamlayacağı sayı. Bu beş ayrı koşumda birebir
aynı kaldı.

## Bekleyen Görevin Sınıfı

`kaynak-kisa-bekleme` ve `kaynak-uzun-bekleme` **yoklanınca** sınıfına giriyor: ikisi de
`kostur`a doğrudan veriliyor, ikisi de kendi `kalan` sayaçlarına göre ilerliyor. `veriyi_oku`
ise, önceki dersteki `disariyi_bekle` ile aynı konuma düşüyor — kendi ilerlemesi `kaynak_c`nin
tamamlanmasına bağlı, dolayısıyla **başkası bitince** sınıfına giriyor. `kaynak_c`nin kendisi
yine **yoklanınca**; iki sınıf burada da, önceki derste olduğu gibi, aynı görev ağacının farklı
katmanlarında bir arada bulunuyor.

Standart Kütüphane ve Ağ Uygulamaları kursu bir ağ sınırını geçen değerin **tip bilgisini**
neyin koruyup neyin kaybettiğini ölçmüştü — sınırın bir tarafındaki tip, öbür tarafa hangi
biçimde ulaşıyordu. Bu ders aynı sınırı başka bir eksenden ölçüyor: bir girdi/çıktı kaynağını
beklerken bir görevin **ilerlemek için neye ihtiyaç duyduğu**. Tip bilgisi burada hiç
tartışılmıyor — `SahteKaynak`in ürettiği değer zaten en baştan `&'static str`; sınır, bir
tipin biçimini değil, bir görevin ilerleme koşulunu ayırıyor.

İki ölçüm de aynı sınırı — bir programın kendi kontrolündeki kod ile dışarıdan gelen bir
kaynak arasındaki sınırı — konu ediniyor, ama sordukları soru farklı. Orada soru "bu sınırı
geçen değer hâlâ aynı tipte mi" idi; burada soru "bu sınırın öbür tarafındaki kaynak hazır
olana kadar bu taraf ne yapıyor" oluyor. `kaynak-kisa-bekleme` ile `kaynak-uzun-bekleme`
arasındaki fark bir tip farkı değil — ikisi de aynı `SahteKaynak` tipinden, ikisinin de
`Output`u aynı; fark yalnız `kalan` alanının başlangıç değerinde, yani ilerleme koşulunun ne
kadar sıklıkla karşılanacağında.

## Yürütücü Burada da Fark Etmiyor

Yürütücüler dersi, iki farklı yürütücü kurgusunun aynı görevi aynı sayıda yokladığını
göstermişti. Aynı sonuç, modellenen bir girdi/çıktı kaynağı için de geçerli. Aşağıdaki kesit,
önceki dersteki `kostur_dongu`yu bu derse taşıyıp aynı `SahteKaynak`i iki farklı yürütücüyle
koşturuyor.

```rust
// ilerle.rs — cekirdegin kesiti degisti: onceki dersteki ikinci yurutucu (kostur_dongu)
// buraya taniniyor, ayni SahteKaynak iki yurutucuyle karsilastiriliyor.

use std::future::Future;
use std::pin::Pin;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, Wake, Waker};

pub struct Uyandirici {
    pub hazir: Mutex<bool>,
    pub kosul: Condvar,
}

impl Wake for Uyandirici {
    fn wake(self: Arc<Self>) {
        *self.hazir.lock().unwrap() = true;
        self.kosul.notify_one();
    }
}

pub fn kostur<F: Future>(gorev: F) -> (F::Output, u32) {
    let mut gorev = Box::pin(gorev);
    let u = Arc::new(Uyandirici {
        hazir: Mutex::new(false),
        kosul: Condvar::new(),
    });
    let waker: Waker = u.clone().into();
    let mut baglam = Context::from_waker(&waker);
    let mut yoklama = 0;
    loop {
        yoklama += 1;
        match gorev.as_mut().poll(&mut baglam) {
            Poll::Ready(x) => return (x, yoklama),
            Poll::Pending => {
                let mut h = u.hazir.lock().unwrap();
                while !*h {
                    h = u.kosul.wait(h).unwrap();
                }
                *h = false;
            }
        }
    }
}

pub struct BosUyandirici;

impl Wake for BosUyandirici {
    fn wake(self: Arc<Self>) {}
}

pub fn kostur_dongu<F: Future>(gorev: F) -> (F::Output, u32) {
    let mut gorev = Box::pin(gorev);
    let u = Arc::new(BosUyandirici);
    let waker: Waker = u.clone().into();
    let mut baglam = Context::from_waker(&waker);
    let mut yoklama = 0;
    loop {
        yoklama += 1;
        if let Poll::Ready(x) = gorev.as_mut().poll(&mut baglam) {
            return (x, yoklama);
        }
    }
}

pub struct SahteKaynak {
    pub kalan: u32,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

impl Future for SahteKaynak {
    type Output = &'static str;
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<&'static str> {
        if self.kalan == 0 {
            self.kanal.send(self.ad.to_string()).unwrap();
            Poll::Ready(self.ad)
        } else {
            self.kalan -= 1;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }
}
```

```rust
// iki-yurutucu-io.rs — ayni modellenen kaynak, iki farkli yurutucu, ayni yoklama sayisi.
mod ilerle;
use ilerle::{kostur, kostur_dongu, SahteKaynak};
use std::sync::mpsc::channel;

fn main() {
    let (gonder, al) = channel::<String>();

    let (_deger_1, yoklama_1) = kostur(SahteKaynak {
        kalan: 3,
        ad: "kaynak-kostur",
        kanal: gonder.clone(),
    });
    let (_deger_2, yoklama_2) = kostur_dongu(SahteKaynak {
        kalan: 3,
        ad: "kaynak-dongu",
        kanal: gonder.clone(),
    });

    drop(gonder);
    let mut kayitlar: Vec<String> = al.try_iter().collect();
    kayitlar.sort();

    println!("kostur={} kostur_dongu={} ayni mi: {}", yoklama_1, yoklama_2, yoklama_1 == yoklama_2);
    println!("kayitlar ({}):", kayitlar.len());
    for k in &kayitlar {
        println!("  {}", k);
    }
}
```

```
kostur=4 kostur_dongu=4 ayni mi: true
kayitlar (2):
  kaynak-dongu
  kaynak-kostur
```

**AS6.** Aynı `kalan: 3` değeriyle kurulan iki kaynak, iki bambaşka yürütücüyle koşturulduğunda
birebir aynı yoklama sayısını (4) veriyor. `SahteKaynak`in `poll` yöntemi hangi yürütücünün
onu çağırdığını hiç bilmiyor — yalnız kendi `kalan` alanına bakıyor. Bu, önceki dersteki
bulguyu modellenen bir girdi/çıktı kaynağına taşıyor: yürütücü, bir kaynağın **ne kadar**
bekleteceğine karışmıyor, yalnız beklerken çağıran iş parçacığının ne yaptığına karışıyor —
ve bu ikinci fark, kursun basmayı yasakladığı bir alanda kalıyor.

## Sınırlayıcı Ölçüm

Modellenen bekleme, gerçek bir gecikmenin süresini göstermiyor. `kalan: 4` alanı beş yoklama
gerektiriyor, ama bu beş çağrı arasında gerçekte hiçbir bekleme yok — `kostur`un koşul
değişkeni her `wake_by_ref` çağrısında hemen uyanıyor, çünkü `SahteKaynak` kendini her
`Pending` dönüşünde kendisi uyandırıyor. Gerçek bir kaynak (bir ağ soketi, bir dosya
tanıtıcısı) bu uyandırmayı kendi hazır olduğu anda, işletim sisteminin bir bildirimiyle
yapardı; burada bu bildirim `kalan` sayacının azalmasıyla taklit ediliyor. Ölçülen şey her
zaman **yoklama sayısı** — bekleme süresi değil, çünkü süre ortama bağlı ve bu kursun
başından beri basılmayan bir değer.

Bu sınırın bir sonucu da şu: `kalan` alanına yazılan sayı büyütülürse yoklama sayısı da
orantılı büyür, ama koşumun gerçek süresi ölçülebilir biçimde değişmez — hiçbir yerde gerçek
bir bekleme yok, yalnız döngünün kaç kez çalıştığı değişiyor. Gerçek bir programda bu ilişki
tersine döner: yoklama sayısı sabit kalabilir (bir yürütücü, bir kaynağı yalnız gerçekten
hazır olduğunda tekrar yoklayacak biçimde tasarlanabilir), ama gerçek bekleme süresi kaynağın
kendi hızına bağlı olarak değişir. Bu iki eksenin — yoklama sayısı ve bekleme süresi —
birbirinden bağımsız olabileceğini görmek, modelin neyi temsil edip neyi etmediğini anlamak
için gerekli: bu ders yalnız birincisini, kursun en başından beri basılmasına izin verilen
tek sayıyı ölçüyor.

## Özet

- Girdi/çıktı beklerken duraklama, hazır olmayan bir kaynağı modelleyen bir gelecekle
  kurulur: kaynak hazır değilse `Pending` döner ve kendini uyandırır.
- Bir kaynağın kaç yoklamada tamamlanacağı, kaynağın kendi `kalan` mantığından gelir ve
  belirlenimcidir — beş ayrı koşumda birebir aynı kalır.
- Doğrudan yoklanan bir kaynak **yoklanınca** sınıfına girer; onu bekleyen bir `async fn`in
  kendi tamamlanması **başkası bitince** sınıfına girer.
- Standart Kütüphane ve Ağ Uygulamaları kursu bir sınırı tip bilgisi üzerinden ölçtü; bu ders
  aynı sınırı bir görevin ilerleme koşulu üzerinden ölçer.
- Modellenen bekleme gerçek gecikmeyi göstermez; ölçülen şey yoklama sayısıdır, geçen süre
  değil.
- Aynı modellenen kaynak iki farklı yürütücüyle koşturulduğunda da aynı yoklama sayısını
  verir — Yürütücüler dersinin bulgusu, modellenen girdi/çıktıda da değişmeden geçerlidir.

## Sonraki Adım

Bu konu, bir görevin ilerlemek için neye ihtiyaç duyduğunu dört adımda kurdu: bir görev
kurulmasıyla ilerlemez, yalnız yoklandığında ilerler; `async`/`await` bu yoklamaların nerede
duraklayıp nereden devam edeceğini kaynakta görünür kılar; bir yürütücü bu yoklamaların **ne
zaman** olacağına karar verir ama görevin ürettiği değere karışmaz; ve bir girdi/çıktı kaynağı
bu yoklama zincirini, hazır olma süresine bağlı bir sayıyla uzatabilir. Dört ders boyunca
ölçülen tek sayı — kaç kez `poll` çağrıldığı — her defasında kaynaktan önceden çıkarılabilir
kaldı.

Kalan soru, kaynakta yazılı ama hiç tamamlanmayan ya da tamamlanmadan bırakılan bir görevin ne
olacağı. Kursun kapanış dersi bir görevin **bırakılmasıyla** kurulan iptali ele alıyor:
bırakılan bir görev bir daha hiç yoklanmıyor ve bu dersin `SahteKaynak`i gibi kaynakta yazılı
kalıyor, ama Gelecekler dersindeki `_hic_yoklanmayan` ile aynı akıbeti paylaşıyor — hiç
ilerlemeden. Zaman aşımı da aynı çerçeveden, süreyle değil, yoklama sayısıyla modelleniyor.
