---
title: Yürütücüler
source: 'https://academia.sh/tr/kurslar/rust-eszamanlilik/yurutuculer'
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:
    - 'Bir yürütücünün görevi ne yaptığından bağımsız, yalnız ne zaman yoklanacağına karar veren bir bileşen olduğunu gösterir'
    - 'Aynı görev türünü iki farklı yürütücü kurgusuyla koşturup yoklama sayısının değişmediğini doğrular'
    - 'Bir gövdenin kaç kez koştuğunu sayan Trait''ler, Genellikler ve Hata Yönetimi kursunun ölçtüğü eksenle bu dersin "kim ilerletiyor" sorusu arasındaki farkı açıklar'
---

# Yürütücüler

Rust'ın standart kitaplığında yürütücü yoktur; kursun kendi elle yazdığı iki farklı yürütücü kurgusu aynı görevi koşturduğunda birebir aynı yoklama sayısını verir — yürütücü görevin gövdesine hiçbir şey eklemez, yalnız ne zaman yoklayacağına karar verir.

Önceki iki ders `kostur`u verili bir araç olarak kullandı: bir gelecek verildi, sonucu ve
yoklama sayısı geri geldi. Aradaki mekanizma hiç sorgulanmadı. Bu ders tam olarak o
mekanizmayı açıyor. Rust'ın standart kitaplığında bir yürütücü yoktur — `Future`, `Pin`,
`Context`, `Poll` ve `Waker` birer sözleşme tanımlar, ama bu sözleşmeyi kimin, ne zaman
çağıracağına dair hiçbir karar vermez. Bu boşluğu dolduran kod, kursun ölçüm çekirdeğinde elle
yazılmış durumda ve bu ders onu doğrudan konu ediniyor.

Bu boşluk, dilin bir eksikliği değil, bilinçli bir sınır. Bir yürütücünün doğru kararı, hangi
programın çalıştığına göre değişir: az sayıda uzun süren görevi olan bir program başka bir
zamanlama isterken, binlerce kısa görevi olan bir program başka bir zamanlama ister. Dil, tek
bir politikayı dayatmak yerine sözleşmeyi (`Future`, `Poll`, `Waker`) standartlaştırıp
zamanlamayı ayrı bir bileşene bırakıyor. Bu ders iki farklı yürütücü kurup aynı görevleri her
ikisiyle de koşturarak bu ayrımın kaynakta ne anlama geldiğini gösteriyor: yürütücüyü
değiştirmek, görevi tanımlayan kodun tek bir satırını bile değiştirmiyor.

## Yürütücü Sözleşmenin Dışında Kalan Parçadır

`Future` trait'i yalnız `poll` yöntemini bildirir; bu yöntemi kimin, hangi sırayla, ne kadar
sıklıkla çağıracağını hiçbir yerde söylemez. Bu boşluk kasıtlı: dilin kendisi tek bir
zamanlama politikasını dayatmıyor, zamanlamayı ayrı bir bileşene — yürütücüye — bırakıyor.
Aşağıdaki kesit iki farklı yürütücü tanımlıyor. Birincisi önceki derslerde kullanılan `kostur`
— uyandırılana kadar bir koşul değişkeninde bekleyen kurgu. İkincisi `kostur_dongu` — hiç
beklemeyen, `Pending` döner dönmez hemen yeniden yoklayan bir kurgu.

```rust
// ilerle.rs — cekirdegin kesiti: iki farkli yurutucu kurgusu, ayni gorev turleri.

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();
    }
}

// yurutucu 1: uyandirilana kadar bekler
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>) {}
}

// yurutucu 2: uyanma sinyalini beklemez, hemen yeniden yoklar
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 BirKezDuraklar {
    pub duraklandi: bool,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

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

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

impl Future for HemenHazir {
    type Output = &'static str;
    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<&'static str> {
        self.kanal.send(self.ad.to_string()).unwrap();
        Poll::Ready(self.ad)
    }
}
```

İki yürütücü de aynı üç adımı izliyor: yoklama sayacını artır, `poll`u çağır, sonucu
değerlendir. Ayrıldıkları tek yer `Pending` dönüşünde ne yaptıkları. `kostur`, `Uyandirici`nin
taşıdığı koşul değişkeninde gerçekten bekliyor ve yalnız `wake` çağrıldığında devam ediyor.
`kostur_dongu` ise `BosUyandirici` adlı, `wake` çağrısında hiçbir şey yapmayan bir tip
kullanıyor ve `Pending` her döndüğünde beklemeden yeniden `poll` çağırıyor. İkisi de aynı
`Future` sözleşmesine uyuyor; ikisi de geçerli birer yürütücü. Aralarındaki fark, görevin
kendisinde değil, görevi **ne zaman** tekrar yoklayacaklarında.

İki `Wake` gerçekleştirimi arasındaki fark da bu tercihi yansıtıyor. `Uyandirici`, bir
mantıksal bayrak ve bir koşul değişkeni taşıyarak `wake` çağrısını bekleyen tarafa gerçekten
iletiyor — kim çağırırsa çağırsın, `kostur` bu çağrıyı görecek ve uyanacak. `BosUyandirici`
ise `wake`i hiç kullanmıyor; gövdesi boş, çünkü `kostur_dongu` zaten `wake` çağrısını
beklemeden yeniden yokluyor. `Waker`in kendisi hâlâ her iki gelecek türüne veriliyor —
`BirKezDuraklar`ın `poll`u kendi `Context`ine hangi yürütücünün taşıdığı hangi `Wake`
gerçekleştirimini içerdiğini bilmeden, koşulsuz olarak `wake_by_ref` çağırıyor. Sözleşmeye
uymak bu kadarını istiyor; ötesi yürütücünün kendi kararı.

Her iki yürütücü de kendi uyandırıcısını bir `Arc` içinde tutuyor. Sahiplik ve Bellek Modeli
kursunun ölçtüğü paylaşımlı sahiplik burada tam olarak kullanım yerini buluyor: `Waker`,
`Context::from_waker` içinden geleceğin `poll` yöntemine bir referans olarak geçiyor, ama
uyandırıcının kendisi yürütücünün döngüsünde de yaşamaya devam etmesi gerekiyor. `Arc`,
uyandırıcının hem `Waker`in içinde hem yürütücünün kendi değişkeninde aynı anda var
olabilmesini sağlıyor — hangisi önce bırakılırsa bırakılsın, öbürü elindeki kopyayı
kullanmaya devam edebiliyor. `BosUyandirici` da aynı düzeni izliyor, tuttuğu veri boş olsa
bile.

## Aynı Görev, İki Yürütücü, Aynı Yoklama Sayısı

Aşağıdaki program hem bir kez duraklayan hem de hemen hazır olan görevleri, aynı tanımdan
kurup her ikisini de iki ayrı yürütücüyle koşturuyor.

```rust
// iki-yurutucu.rs — ayni gorev turu, iki farkli yurutucu, ayni yoklama sayisi.
mod ilerle;
use ilerle::{kostur, kostur_dongu, BirKezDuraklar, HemenHazir};
use std::sync::mpsc::channel;

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

    let (deger_a1, yoklama_a1) = kostur(BirKezDuraklar {
        duraklandi: false,
        ad: "duraklar-kostur",
        kanal: gonder.clone(),
    });
    let (deger_a2, yoklama_a2) = kostur_dongu(BirKezDuraklar {
        duraklandi: false,
        ad: "duraklar-dongu",
        kanal: gonder.clone(),
    });

    let (deger_b1, yoklama_b1) = kostur(HemenHazir {
        ad: "hazir-kostur",
        kanal: gonder.clone(),
    });
    let (deger_b2, yoklama_b2) = kostur_dongu(HemenHazir {
        ad: "hazir-dongu",
        kanal: gonder.clone(),
    });

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

    println!(
        "bir kez duraklayan: kostur={} kostur_dongu={} (deger {} / {})",
        yoklama_a1, yoklama_a2, deger_a1, deger_a2
    );
    println!(
        "hemen hazir:        kostur={} kostur_dongu={} (deger {} / {})",
        yoklama_b1, yoklama_b2, deger_b1, deger_b2
    );
    println!(
        "ayni yoklama sayisi mi: duraklayan={} hazir={}",
        yoklama_a1 == yoklama_a2,
        yoklama_b1 == yoklama_b2
    );
    println!();
    println!("kayitlar ({}):", kayitlar.len());
    for k in &kayitlar {
        println!("  {}", k);
    }
}
```

```
bir kez duraklayan: kostur=2 kostur_dongu=2 (deger duraklar-kostur / duraklar-dongu)
hemen hazir:        kostur=1 kostur_dongu=1 (deger hazir-kostur / hazir-dongu)
ayni yoklama sayisi mi: duraklayan=true hazir=true

kayitlar (4):
  duraklar-dongu
  duraklar-kostur
  hazir-dongu
  hazir-kostur
```

**AS4.** Dört ayrı çağrı, iki farklı yürütücü üzerinden iki farklı görev türünü koşturuyor ve
sonuç aynı kalıyor: bir kez duraklayan görev her iki yürütücüde de iki yoklamada, hemen hazır
olan görev her iki yürütücüde de bir yoklamada bitiyor. Bu beş ayrı koşumda birebir aynı kaldı.
`BirKezDuraklar`ın gövdesi hiç değişmedi — hangi yürütücünün onu çağırdığını bilmiyor bile,
`Context` içindeki `Waker`i çağırmaktan başka bir şey yapmıyor. Yoklama sayısını belirleyen,
görevin kendi `poll` mantığı; yürütücü yalnız bu mantığı **ne sıklıkla** tetikleyeceğine karar
veriyor.

## Kaç Görev Aynı Anda Koşuyor

`kostur` ve `kostur_dongu` her çağrıda tek bir kök gelecek alıyor ve o gelecek bitene kadar
çağıran iş parçacığını bırakmıyor. Bu, gerçek bir yürütücünün genellikle yaptığı işin
yalınlaştırılmış hâli: bir yürütücü çoğunlukla aynı anda birden fazla göreve bakan bir kuyruk
tutar, hangisinin yoklanacağına o kuyruktan seçerek karar verir. Bu dersteki dört çağrı — iki
yürütücünün her biriyle koşturulan `BirKezDuraklar` ve `HemenHazir` örnekleri — birbirinden
bağımsız, art arda çalışıyor; hiçbiri bir ötekiyle aynı anda yoklanmıyor. Yürütücünün
**birden fazla** görevi aynı anda gözetmesi
ayrı bir tasarım sorusu ve bu dersin ölçümünün dışında kalıyor — burada sabitlenen, tek bir
görevi ilerletmenin en yalın biçimi. İki yürütücü kurgusunun aynı sayıyı vermesi, bu yalın
biçimde bile yürütücünün seçiminin gövdeye hiçbir şey eklemediğini göstermeye yetiyor. Bir
yürütücü birden fazla görevi aynı anda gözetmeye başladığında ortaya çıkan yeni soru —
hangisinin önce yoklanacağı, birinin ötekini ne kadar bekleteceği — bu dersin ölçtüğü
tek-görevli sayıyı değiştirmiyor; yalnız görevler arasındaki **sırayı** ilgilendiren, bu
dersin dışında kalan ayrı bir soru.

## Yürütücü Ne Yaptığını Değil, Ne Zaman Yoklayacağını Belirler

`kostur` ile `kostur_dongu` arasındaki fark, koşumun **gözlemlenebilir** kısmında hiç
görünmüyor: aynı değerler, aynı yoklama sayıları, aynı kayıt kümesi. Fark yalnız `Pending`
dönüşü ile bir sonraki `poll` çağrısı arasında geçen — ve bu kursun basmayı yasakladığı —
sürede yaşıyor. `kostur`, çağıran iş parçacığını bu süre boyunca gerçekten uyutuyor;
`kostur_dongu` aynı iş parçacığını meşgul bir döngüde tutuyor. İkisi de doğru sonucu üretiyor,
ama biri işlemciyi bekleme sırasında serbest bırakıyor, öbürü bırakmıyor. Bu, iş parçacıklarını
ele alan konuda ölçülen bir sorunun — bir iş parçacığının beklerken ne yaptığının — burada
yürütücü düzeyinde tekrar karşımıza çıkan biçimi; ama bu ders bunu **süre** üzerinden değil,
**yoklama sayısının değişip değişmediği** üzerinden ölçüyor ve değişmediğini gösteriyor.

Trait'ler, Genellikler ve Hata Yönetimi kursu bir gövdenin kaç kez koştuğunu ölçmüştü — her
seferinde, yalnız gerektiğinde ya da hiç. Bu ders farklı bir soru soruyor: aynı sayıda koşan
bir gövdeyi **kim** ilerletiyor. Yürütücü, tam olarak bu sorunun yanıtı — bir gövdenin kaç kez
koştuğuna karışmayan, yalnız o koşuşların ne zaman gerçekleşeceğine karar veren özne. `kostur`
ile `kostur_dongu` bu özne rolünü iki farklı biçimde oynuyor, ama ikisi de aynı sayıda
tetikliyor.

Bu iki kurgu arasındaki seçim, gerçek bir programda önemsiz değil. `kostur_dongu`, meşgul bir
döngüyle işlemciyi sürekli meşgul tutuyor; bir görev `Pending` döndürdüğü anda hazır olmasa
bile döngü onu tekrar tekrar sorguluyor. `kostur` ise işlemciyi koşul değişkeni beklerken
serbest bırakıyor ve yalnız `wake` çağrıldığında geri dönüyor. İkisi de bu derste ölçülen
yoklama sayısını değiştirmiyor, çünkü `BirKezDuraklar`ın `Pending` dönme kararı zaten
`duraklandi` alanına bakılarak veriliyor — dışarıdan ne kadar sık sorulduğuna bakmadan aynı
kararı veriyor. Gerçek bir kaynağı bekleyen bir gelecekte (bir ağdan cevap, bir dosyadan
veri) bu fark daha da belirginleşir, ama bu dersin ölçtüğü sayı üzerinde hâlâ hiçbir etkisi
olmaz — sıradaki ders tam olarak böyle bir kaynağı modelliyor.

## Aynı Yürütücü, İki Farklı Gelecek Türü

`kostur` ve `kostur_dongu` her ikisi de `F: Future` sınırıyla genel yazıldı; bu yüzden aynı
işlev hem `BirKezDuraklar` hem `HemenHazir` için, tek bir gövde değişikliği yapmadan
çağrılabiliyor. Trait'ler, Genellikler ve Hata Yönetimi kursu genel bir gövdenin her somut tip
için derleyici tarafından ayrı üretildiğini ölçmüştü; burada da aynı gerçekleşiyor —
`kostur::<BirKezDuraklar>` ile `kostur::<HemenHazir>` derleyicinin ürettiği iki ayrı gövde. Bu
ders o eksene dönmüyor, ama sonucu ondan yararlanıyor: yürütücü kodu tek bir yerde yazılıyor,
her yeni gelecek türü için yeniden yazılmasına gerek kalmıyor. `iki-yurutucu.rs`deki dört
çağrının hepsi aynı iki işlevi (`kostur`, `kostur_dongu`) çağırıyor; hiçbiri yürütücünün
gövdesini kopyalamıyor.

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

Yürütücüyü değiştirmek görevin gövdesini değiştirmiyor. `BirKezDuraklar`ın `poll` yöntemi
hangi yürütücünün onu çağırdığına bakmıyor bile — `duraklandi` alanının değerine bakıyor ve
kendi kararını veriyor. Aynı görev iki kurguda aynı kaydı üretiyor; fark yalnız yoklama
düzeninde kalıyor, üretilen değerde ya da kayıtta değil. Bu sınır, bir yürütücünün neyi
**değiştiremeyeceğini** gösteriyor: bir görevin ne ürettiğini, kaç adımda ürettiğini ya da
hangi sırayla dallandığını. Yürütücü yalnız `poll` çağrısının **zamanlamasını** değiştirebilir;
bunun ötesinde bir görev üzerinde hiçbir yetkisi yoktur.

Bu sınır, birden fazla görevin aynı yürütücüde bir arada bulunduğu durumlarda daha görünür
hale gelir: yürütücü, hangi görevin **önce** yoklanacağına karar verebilir, ama bir görevin
kendi `poll`u içinde kaç kez `Pending` döneceğine karışamaz. Bu dersteki tek görevlik
örneklerde bu ayrım gizli kalıyor çünkü sırayı belirleyecek başka bir görev yok; ama kural
zaten burada da geçerli — `kostur` ile `kostur_dongu` birbirinden çok farklı zamanlama
stratejileri izlese de, `BirKezDuraklar`ın iki yoklamada bitmesi kararını hiçbiri değiştiremedi.

## Özet

- Rust'ın standart kitaplığında yürütücü yoktur; `poll`u çağıracak döngü elle yazılır.
- Bir yürütücü, `Pending` dönüşünde ne yapacağına karar veren bileşendir — beklemek,
  hemen yeniden yoklamak ya da başka bir strateji izlemek yürütücünün seçimidir.
- Aynı görev türü iki farklı yürütücüyle koşturulduğunda aynı yoklama sayısını verir: yürütücü
  görevin gövdesine hiçbir şey eklemez.
- Yürütücü, bir gövdeyi ilerleten **özne**dir; Trait'ler, Genellikler ve Hata Yönetimi
  kursunun ölçtüğü "kaç kez koştu" sorusundan farklı olarak, bu ders "kim ilerletti" sorusunu
  sorar.
- Bu derste koşturulan dört görev de **yoklanınca** sınıfına girer: hiçbiri kendi başına
  ilerlemiyor, dördü de bir yürütücünün `poll` çağrısına ihtiyaç duyuyor — değişen yalnız
  hangi yürütücünün bu çağrıyı yaptığı, sınıfın kendisi değil.
- İki yürütücü arasındaki gerçek fark koşum sayısında değil, `Pending` dönüşü ile yeniden
  yoklama arasında geçen — ve basılmayan — süredeki davranışta yaşar; `kostur` ve
  `kostur_dongu` genel (`F: Future`) yazıldığı için aynı yürütücü gövdesi her gelecek türüyle
  çalışır.

## Sonraki Adım

Bu ders, bir yürütücünün görevin **ne ürettiğine** karışmadığını, yalnız **ne zaman**
yoklandığına karar verdiğini gösterdi; iki bambaşka zamanlama stratejisi, aynı görev
üzerinde birebir aynı yoklama sayısını verdi. Buraya kadar her görev ya hemen hazırdı ya da
bir kez duraklayıp kendi kendine devam etti — ikisinde de duraklamanın nedeni görevin kendi
mantığıydı, dışarıdan bir koşula bağlı değildi. Sıradaki ders, dışarıdan bir kaynağın — girdi
ya da çıktının — henüz hazır olmadığı durumu ele alıyor: hazır olmayan bir kaynak `Pending`
döner, hazır olduğunda yürütücüyü uyandırır. Ölçülen yine yoklama sayısı, ama bu kez o sayı
kaynağın hazır olma süresine bağlı olarak değişiyor ve bu ders kurulan iki yürütücüden birini
— `kostur`u — yeniden kullanıyor.
