İçeriğe geç
academia.sh

Ders 10 / 10

İptal ve Zaman Aşımı

Kursun kapanış dersi bir görevin bırakılmasıyla kurulan iptali ölçüyor: bırakılan görev bir daha yoklanmaz ve hiç sınıfına düşer; zaman aşımı da bir süre değil, tükenen bir yoklama bütçesiyle modellenir.

Yayın Güncelleme

Bu derste

  • Bir görevin bırakılmasıyla kurulan iptali, bir daha yoklanmama durumu olarak tanımlar
  • Zaman aşımını süre yerine tükenen bir yoklama bütçesiyle modelleyip bütçesi biten görevin nasıl bırakıldığını gösterir
  • Kursun on dersini kapanış tablosunda birleştirip her birimin ilerleme koşulunu kaynak metinden doğrular
İçindekiler

Eşzamansız Girdi/Çıktı dersi kaynakta yazılı ama hiç tamamlanmayan bir görevin sorusunu açık bıraktı: bir görev sonsuza kadar duraklayabilir, peki ona bir daha hiç dokunulmazsa ne olur? Bu kursun kapanış dersi bu soruyu ele alıyor. Rust’ın eşzamansız modelinde bir görevi durdurmanın ayrı bir çağrısı yok — bir gelecek yalnızca yoklandığı sürece ilerler, bu yüzden onu bir daha yoklamamak, o görevi iptal etmenin ta kendisi oluyor.

Go’nun Eşzamanlılık kursu iptali bir goroutine’in nasıl duracağını belirleyen dört yoldan biri, yani bir durma düzeneği olarak ölçtü. Bu ders aynı kavramı başka türlü ölçüyor: iptal edilen bir görevin kendi başına bir yürütme akışı yok ki dursun — iptal burada bir ilerlememe durumu, kaynakta yazılı kalıp bir daha yoklanmayan bir görevin akıbeti. Ölçülen iki ayrı yol var: bir görev elle bırakılarak iptal edilebilir, ya da bir yoklama bütçesi tükendiğinde otomatik olarak bırakılarak zaman aşımına uğrayabilir. İkisi de aynı sınıfa düşüyor.

Bırakılan Bir Görev Bir Daha Yoklanmaz

Bir görevin bırakılması tek başına iptal anlamına gelmiyor. Tamamlanmış bir görev de, ürettiği değer okunduktan sonra bellekte durmaya devam etmiyor — o da bırakılıyor, ama bu bir temizlik, bir iptal değil. Ayrım, bırakma anında görevin bitmiş olup olmadığında: bitmeden bırakılan bir görev bir daha hiç yoklanmayacak, bitmiş bir görevin zaten yoklanacak bir şeyi kalmamış durumda. Aşağıdaki çekirdek kesiti bu ayrımı iki alanla tutuyor: baslatildi görevin en az bir kez yoklandığını, tamamlandi son değerini üretip üretmediğini işaretliyor. Yalnızca ilki doğru, ikincisi yanlışken bırakılan bir görev iptal edilmiş sayılıyor; bu, görevin kendi Drop uygulamasında bir kayıt gönderilerek ölçülüyor.

// ilerle.rs — cekirdegin kesiti: birakma ile kurulan iptal ve yoklama butcesiyle
// modellenen zaman asimi.

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

// bir kez yoklandiktan sonra elle birakilabilen gorev: baslamis is olculebilir,
// tamamlanma ise ancak ikinci yoklama gerceklesirse kayda geciyor.
pub struct IptalEdilebilirIs {
    pub ad: &'static str,
    pub baslatildi: bool,
    pub tamamlandi: bool,
    pub kanal: Sender<String>,
}

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

impl Drop for IptalEdilebilirIs {
    fn drop(&mut self) {
        if self.baslatildi && !self.tamamlandi {
            self.kanal.send(format!("{}-birakildi", self.ad)).unwrap();
        }
    }
}

// her yoklamada bir azalan bir sayacla ilerleyen uzun bir is; tamamlanmadan
// birakilirsa kalan miktar sifirdan buyuk kalir.
pub struct UzunIs {
    pub kalan: u32,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

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

impl Drop for UzunIs {
    fn drop(&mut self) {
        if self.kalan > 0 {
            self.kanal.send(format!("{}-yarida-birakildi", self.ad)).unwrap();
        }
    }
}

// ic gorevi sinirli sayida yoklama hakkiyla saran gorev: hak tukenince ic gorev
// hemen birakilir ve bir daha yoklanmaz.
pub struct ZamanAsimliGorev<F: Future> {
    pub ic: Option<Pin<Box<F>>>,
    pub hak: u32,
    pub ad: &'static str,
    pub kanal: Sender<String>,
}

impl<F: Future> Future for ZamanAsimliGorev<F> {
    type Output = Result<F::Output, ()>;
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<F::Output, ()>> {
        if self.hak == 0 {
            self.ic = None;
            self.kanal.send(format!("{}-zaman-asimi", self.ad)).unwrap();
            return Poll::Ready(Err(()));
        }
        self.hak -= 1;
        match self.ic.as_mut().unwrap().as_mut().poll(cx) {
            Poll::Ready(x) => Poll::Ready(Ok(x)),
            Poll::Pending => Poll::Pending,
        }
    }
}
// iptal.rs — birakma ile kurulan iptal ve yoklama butcesiyle modellenen zaman asimi
// tek kosumda.
mod ilerle;
use ilerle::{kostur, IptalEdilebilirIs, UzunIs, ZamanAsimliGorev, Uyandirici};
use std::future::Future;
use std::sync::mpsc::channel;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Waker};

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

    // --- tamamlanan gorev: kostur onu bitene kadar yokluyor ---
    let (sonuc, yoklama) = kostur(IptalEdilebilirIs {
        ad: "gorev-tamamlanan",
        baslatildi: false,
        tamamlandi: false,
        kanal: gonder.clone(),
    });

    // --- iptal edilen gorev: bir kez elle yoklaniyor, sonra birakiliyor ---
    let mut iptal_edilen = Box::pin(IptalEdilebilirIs {
        ad: "gorev-iptal-edilen",
        baslatildi: false,
        tamamlandi: false,
        kanal: gonder.clone(),
    });
    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 _ = iptal_edilen.as_mut().poll(&mut baglam);
    drop(iptal_edilen);

    // --- yoklama butcesiyle zaman asimi: biri butce icinde bitiyor, digeri asiyor ---
    let (sonuc_a, but_yoklama_a) = kostur(ZamanAsimliGorev {
        ic: Some(Box::pin(UzunIs { kalan: 1, ad: "is-a", kanal: gonder.clone() })),
        hak: 3,
        ad: "is-a",
        kanal: gonder.clone(),
    });
    let (sonuc_b, but_yoklama_b) = kostur(ZamanAsimliGorev {
        ic: Some(Box::pin(UzunIs { kalan: 5, ad: "is-b", kanal: gonder.clone() })),
        hak: 2,
        ad: "is-b",
        kanal: gonder.clone(),
    });

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

    println!("gorev-tamamlanan {} yoklamada bitti, sonuc: {}", yoklama, sonuc);
    println!("gorev-iptal-edilen 1 yoklamadan sonra elle birakildi");
    println!();
    println!("is-a (hak=3, kalan=1) -> {} yoklamada sonuc: {:?}", but_yoklama_a, sonuc_a);
    println!("is-b (hak=2, kalan=5) -> {} yoklamada sonuc: {:?}", but_yoklama_b, sonuc_b);
    println!();
    println!("kayitlar ({}):", kayitlar.len());
    for k in &kayitlar {
        println!("  {}", k);
    }
}
gorev-tamamlanan 2 yoklamada bitti, sonuc: gorev-tamamlanan
gorev-iptal-edilen 1 yoklamadan sonra elle birakildi

is-a (hak=3, kalan=1) -> 2 yoklamada sonuc: Ok("is-a")
is-b (hak=2, kalan=5) -> 3 yoklamada sonuc: Err(())

kayitlar (7):
  gorev-iptal-edilen-baslatildi
  gorev-iptal-edilen-birakildi
  gorev-tamamlanan-baslatildi
  gorev-tamamlanan-tamamlandi
  is-a-tamamlandi
  is-b-yarida-birakildi
  is-b-zaman-asimi

AS7. gorev-tamamlanan iki yoklamada bitiyor — bir duraklama, bir tamamlanma — ve async/await dersinden tanıdık n+1n+1 kuralını sürdürüyor. gorev-iptal-edilen yalnız bir kez elle yoklanıyor, -baslatildi kaydını gönderiyor, sonra doğrudan bırakılıyor; bu bırakma Drop uygulamasını tetikliyor ve -birakildi kaydını gönderiyor. Bu iki kayıt görevin başlamış işini ölçülebilir kılıyor: bir yoklama gerçekleşti, bir temizlik çalıştı. Ama gorev-iptal-edilen-tamamlandi kaydı yedi kayıt arasında hiç yok — çünkü ikinci yoklama hiç gerçekleşmedi. İptal edilen görev hiç sınıfına giriyor: kaynakta yazılı, bir kez dokunulmuş, ama tamamlanmadan bırakılmış.

Yoklama Bütçesi Tükenince Zaman Aşımı

Zaman aşımı bu kursta bir süreyle değil, bir sayıyla kuruluyor: yukarıdaki kesitteki ZamanAsimliGorev, sardığı görevi en fazla hak kez yoklamaya izin veriyor. Bütçe tükenmeden görev kendi kendine biterse sonuç Ok değeriyle dönüyor; bütçe tükendiğinde ZamanAsimliGorev içindeki görevi hemen bırakıyor — self.ic alanına None atanıyor — ve bir daha ona dokunmuyor. Bu, önceki bölümdeki elle bırakma düzeneğinin otomatikleştirilmiş biçimi: kararı görevin kendisi değil, onu saran bütçe veriyor.

AS8. is-a üç yoklama hakkıyla kuruldu ve yalnızca ikisini kullandı, Ok("is-a") ile bitti. is-b yalnızca iki yoklama hakkıyla kuruldu ama beş yoklama gerektiriyordu; üçüncü çağrıda bütçe sıfıra düştüğü için sarmalayıcı içteki görevi bırakıyor ve Err(()) döndürüyor. İçteki görevin kendi Drop uygulaması -yarida-birakildi kaydını gönderiyor, çünkü bırakıldığı anda hâlâ üç birimlik işi kalmıştı — beş birimlik işin ikisi tamamlanmış, üçü başlamış ama bitmemiş durumda kalmıştı. is-b-tamamlandi kaydı da yedi kayıt arasında hiç yok. is-a yoklanınca sınıfına giriyor; is-b bütçesi tükendiğinde hiç sınıfına düşüyor — elle bırakılan görevle aynı akıbeti, farklı bir tetikleyiciyle paylaşıyor.

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

Bırakma, görevin iç durumunu göstermiyor. Drop uygulaması yalnızca yazması için elle kodlanmış olanı kaydediyor — IptalEdilebilirIs baslatildi ile tamamlandi bayraklarını, UzunIs kalan sayaç değerini tutuyor. Bu, dilin genel bir “ne kadar iş yapıldı” bilgisini kendiliğinden açığa çıkarmadığı anlamına geliyor: bir görev türünün yazarı bu bilgiyi ölçülebilir kılmazsa, bırakılan bir değerin ne durumda olduğu kaynak kod dışında bilinemez.

Yoklama bütçesi de bir süre değil. hak alanı kaç kez yoklanacağının bir üst sınırı, ama bir görevin gerçek dünyada ne kadar sürdüğüyle hiçbir ilişkisi yok — aynı bütçe, her yoklaması ağır bir görevle de, her yoklaması anlık bir görevle de aynı sayıda yoklama sonra tükeniyor. Gerçek bir zaman aşımı düzeneği bu sayıyı bir saatle eşleştirir; bu kurs saat kullanmadığı için, zaman aşımı burada yalnızca kaç kez denendiğinin bir ölçüsü, ne kadar beklendiğinin değil.

Kapanış Tablosu

Ders Ölçülen birim İlerleme koşulu Sınırlayıcı ölçüm
İş Parçacığı Oluşturma İş parçacığı başlatma ve birleştirme kendi başına · başkası bitince Kayıtların sırası ölçülmez, yalnız beklenen sayı var mı ölçülür
Kanallarla Mesajlaşma Kanaldan geçen iletinin sahipliği ve alımı kendi başına · başkası bitince Bekleyen taraf süresiz bekler; eksik kayıt beklenen-gelen farkıyla ölçülür
İş Parçacıkları Arası Güvenliğin Tipteki Karşılığı Sınırı geçebilen Arc<Mutex<...>> değeri kendi başına · başkası bitince Tipin izni veri yarışını engeller, sıraya bağlı doğruluğu garanti etmez
Muteks ve Okuma/Yazma Kilidi Kilit bekleyen taraf kendi başına · başkası bitince İki bekleyenden hangisi önce girer ölçülmez, kilit yalnız veriyi korur
Atomik İşlemler ve Bellek Sıralaması Kilitsiz atomik sayaç kendi başına Atomik olmak sıralı olmak değildir; hangi artış önce ölçülmez
Gelecekler (futures) Yoklanan ve hiç yoklanmayan görev yoklanınca · hiç Yoklanmayan görev hata değildir, sessizce iz bırakmaz
async/await Duraklama noktası ve yoklama sayısı yoklanınca · başkası bitince Duraklama sayısı ölçülür, ne kadar duraklayacağı ölçülmez
Yürütücüler Aynı görev, iki yürütücü kurgusu yoklanınca Yürütücü ne zaman yoklayacağına karışır, görevin gövdesine karışmaz
Eşzamansız Girdi/Çıktı Modellenmiş girdi/çıktı kaynağı yoklanınca · başkası bitince Modellenen bekleme gerçek gecikmeyi göstermez, ölçülen yoklama sayısı
İptal ve Zaman Aşımı Bırakılan ve zaman aşımına uğrayan görev hiç Bırakma iç durumu göstermez; bütçe keyfi bir yoklama sınırıdır

Özet

  • Bir görevi bırakmak, Rust’ın eşzamansız modelindeki iptal çağrısıdır; bırakılan görev bir daha yoklanmaz ve hiç sınıfına düşer.
  • Bir bırakma yalnızca görev tamamlanmadan önce gerçekleşirse iptaldir; tamamlanmış bir görevin bırakılması sıradan bir temizliktir — iki durum bu derste ayrı kayıtlarla ayrıştı.
  • Zaman aşımı bir süre değil bir yoklama bütçesiyle modellendi: bütçesi tükenen görev hemen bırakıldı ve bir daha yoklanmadı — o da hiç sınıfına düştü.
  • İptal edilen görevlerin başlamış işi ölçülebilir kaldı — başladığını ve bırakıldığını gösteren kayıtlar vardı — ama tamamlandığını gösteren kayıt hiçbir zaman gelmedi.
  • Go’nun Eşzamanlılık kursu iptali bir durma düzeneği olarak ölçtü; bu ders aynı kavramı bir ilerlememe durumu olarak ölçtü.
  • Kapanış tablosu, kursun on dersini ve dört sınıfını (kendi başına, yoklanınca, başkası bitince, hiç) tek bir bakışta topladı.

Kurs Kapanışı

Bu kurs on ders boyunca tek bir soruyu sürdürdü: bir birim ilerlemek için neye ihtiyaç duyuyor. Yanıt hep aynı dört isimden biriydi ve kapanış tablosu bu dört ismin on farklı birimde nasıl dağıldığını gösterdi — iş parçacıkları kendi başlarına ya da birbirlerini bekleyerek ilerledi, kilitler ve kanallar bu bekleyişi taşıdı, gelecekler yalnız yoklandıklarında ilerledi ve bu son ders bir görevin hiç ilerlemeyebileceğini — kaynakta yazılı kalıp bir daha dokunulmayarak — gösterdi. Bu, deponun beşinci ve şimdilik son eşzamanlılık kursuydu; beş kurs birlikte aynı alanı beş farklı soruyla dolaştı.

Sıradaki kurs, Rust Ekosistemi ve İleri Konular, dilin eşzamanlılık dışındaki geri kalanına bakıyor: makrolar, derleyici güvenliğinin gönüllü olarak dışına çıkılan kod kesimleri, başka dillerle kurulan çağrı arayüzleri ve bir Rust projesinin paket yönetiminden yayınına kadarki yaşam döngüsü. Bu derste ölçülen ilerleme sorusu — bir birimin ilerlemek için neye ihtiyaç duyduğu — orada yeniden sorulmuyor; sıradaki kurs kendi ölçü eksenini kendi konusundan kuracak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat