İçeriğe geç
academia.sh

Ders 01 / 17

Tek İş Parçacıklı Model

JavaScript'in tek iş parçacıklı, sonuna kadar çalışan yürütme modeli; engelleyen işin sonuçları; dilin çekirdeği ile çalışma zamanının sağladığı eşzamansız yeteneklerin ayrımı.

İçindekiler

Önceki iki kurs dilin değer modelini kurdu: değerler, nesneler, prototip zinciri ve kapanışlar. Bunların tümü tek bir varsayım altında anlatıldı — kod yazıldığı sırada çalışır, bir deyim biter, sonraki başlar. Bu kurs o varsayımı kaldırır.

Bir ölçüm istasyonundan veri istediğinizde yanıt hemen gelmez. Yanıt beklenirken program ne yapar? “Bekler” yanıtı, tek iş parçacıklı bir dilde çok pahalıya mal olur. Bu ders, bekleme kararının neden verilemeyeceğini ve dilin bunun yerine ne yaptığını kurar.

Tek İş Parçacığı Ne Anlama Gelir

Bir iş parçacığı (thread), komutları sırayla yürüten tek bir yürütme akışıdır. Bir JavaScript programında bu akıştan yalnız bir tane vardır: herhangi bir anda kullanıcı kodunun tek bir satırı çalışır. İki fonksiyon aynı anda ilerlemez, iki döngü iç içe geçmez, iki geri çağrı çakışmaz.

Bunun doğrudan bir sonucu vardır: veri yarışı (data race) yoktur. Bir değişkeni okuyup yazdığınız iki satır arasına başka bir kod giremez. Diğer dillerde kilit gerektiren bir sayaç artırımı, burada gerekmez.

İşletim sisteminin süreç ve iş parçacığı kavramları, çekirdek düzeyindeki zamanlama ve gerçek paralellik ayrı bir konudur; İşletim Sistemi Kavramları kursunda ele alınır. Bu kurs, tek bir iş parçacığına düşen kod üzerinde durur.

Sonuna Kadar Çalışma

Modelin ikinci kuralı, tek iş parçacığı kadar bağlayıcıdır: bir kez çalışmaya başlayan bir iş, kendisi bitene kadar bölünmez. Buna sonuna kadar çalışma (run-to-completion) denir.

Bunun anlamı, “sıfır milisaniye sonra çalıştır” diye kaydedilmiş bir işin bile önündeki işin bitmesini beklemesidir.

function toplamHesapla(tur) {
  let toplam = 0;
  for (let i = 0; i < tur; i += 1) toplam += i % 7;
  return toplam;
}

setTimeout(() => console.log("3 — zamanlayıcı geri çağrısı"), 0);

console.log("1 — hesap başlıyor");
const sonuc = toplamHesapla(5_000_000);
console.log("2 — hesap bitti, sonuç:", sonuc);
1 — hesap başlıyor
2 — hesap bitti, sonuç: 14999995
3 — zamanlayıcı geri çağrısı

Geri çağrı en başta kaydedildi ve gecikmesi sıfırdı; buna karşın en sonda çalıştı. setTimeout bir işi şimdi çalıştırmaz, sıraya koyar. Sıradaki iş ancak yürüyen kod bittikten sonra ele alınır. Bu ilkenin ayrıntısı sonraki derste açılacak.

Sonuna kadar çalışma, tek iş parçacığının doğal eşlikçisidir. Yürüyen bir fonksiyonun ortasında başka bir geri çağrının çalışabildiğini varsayarsanız, kilitlere ve yeniden girilebilirlik (reentrancy) tartışmasına geri dönersiniz. Model, bu karmaşıklığı program yazarından uzak tutmak için basitliği seçer — bedeli, sıradaki her işin öndekini beklemesidir.

Engelleyen İş

Engelleyen (blocking) iş, iş parçacığını başka hiçbir şey yapamayacak biçimde meşgul eden iştir. İki kaynağı vardır.

Birincisi uzun süren hesaplamadır: milyonlarca elemanlı bir dizinin sıralanması, büyük bir metnin ayrıştırılması. Bunlar işi gerçekten yapar, yalnız uzun sürer.

İkincisi ve daha sinsi olanı, eşzamanlı (synchronous) girdi-çıktı çağrılarıdır. Bir dosyayı eşzamanlı okuyan bir çağrı, disk yanıt verene kadar iş parçacığını tutar; oysa bu sürede iş parçacığı hiçbir hesap yapmaz, yalnız bekler.

import { writeFileSync, readFileSync, readFile } from "node:fs";

writeFileSync("olcumler.txt", "A1;21.4\nB2;19.8\nC3;23.1\n");

readFile("olcumler.txt", "utf8", (hata, veri) => {
  console.log("3 — eşzamansız okuma bitti:", veri.trim().split("\n").length, "satır");
});

const icerik = readFileSync("olcumler.txt", "utf8");
console.log("1 — eşzamanlı okuma bitti:", icerik.trim().split("\n").length, "satır");
console.log("2 — betiğin geri kalanı");
1 — eşzamanlı okuma bitti: 3 satır
2 — betiğin geri kalanı
3 — eşzamansız okuma bitti: 3 satır

İki okuma da aynı dosyayı okur ve aynı sonucu üretir. Ayrıldıkları nokta, bekleme sırasında iş parçacığının kime ait olduğudur. Eşzamanlı olan onu tutar; eşzamansız olan bırakır ve sonucu hazır olduğunda geri çağrıyla haber verir.

Engellemenin görünen sonucu ortama göre değişir. Tarayıcıda arayüz donar: tıklama işlenmez, çizim güncellenmez, kaydırma takılır. Sunucuda ise tek bir isteğin uzun hesabı, o sırada gelen bütün istekleri kuyrukta bekletir. Her iki durumda da nedeni aynıdır: iş parçacığı tek, sıradaki iş de sabırlı.

Dil Nerede Biter, Çalışma Zamanı Nerede Başlar

Dilin çekirdeği eşzamansızlığı tanımlamaz. Standart, sözleri (promise) ve async fonksiyonları tanımlar; ancak “on milisaniye sonra” veya “dosya okunduğunda” gibi kavramların hiçbiri dilin sözdiziminde yer almaz.

Bu yetenekleri ana ortam (host environment) sağlar. JavaScript Temelleri kursunda tanıtılan ayrım burada işlevsel hale gelir:

Katman Sağladığı şey
Dil çekirdeği Değerler, fonksiyonlar, sözler, async/await, mikro görev kuralı
Ana ortam Zamanlayıcılar, ağ istekleri, dosya erişimi, olaylar, görev kuyruğu, olay döngüsü

setTimeout bir dil sözcüğü değildir; ana ortamın kullanıma açtığı bir fonksiyondur. Aynı biçimde ağ isteği başlatan fonksiyon da ana ortama aittir. Bu nedenle aynı JavaScript kodu farklı ortamlarda farklı yeteneklerle karşılaşır: tarayıcıda belge nesnesi vardır, sunucu çalışma zamanında dosya sistemi vardır.

Ayrım, sorumluluğu da böler. Beklemeyi dil yapmaz. Zamanlayıcıyı ana ortam kurar, süre dolduğunda geri çağrıyı kuyruğa koyar; iş parçacığı boşaldığında JavaScript o geri çağrıyı çalıştırır. Bekleme sırasında iş parçacığı serbesttir.

Eşzamansızlık Paralellik Değildir

Bu ayrım kursun tamamında geçerlidir ve baştan yerleşmelidir.

Paralellik, iki işin aynı anda yürütülmesidir; birden çok iş parçacığı ister. Eşzamansızlık, bir işin başlatılıp sonucunun sonra alınmasıdır; tek iş parçacığıyla da mümkündür.

Üç ölçüm isteğini “aynı anda” başlattığınızda, bu isteklerin ağ üzerindeki bekleme süreleri gerçekten üst üste biner — çünkü beklemeyi yapan ana ortamdır, JavaScript değil. Ama yanıtları işleyen kodunuz sırayla çalışır: birinci yanıt işlenirken ikincisi kuyrukta bekler. Böylece programınız beklemede paralel, hesapta sıralıdır.

Bunun pratik sonucu şudur: eşzamansızlık, girdi-çıktı ağırlıklı işi hızlandırır; hesap ağırlıklı işi hızlandırmaz. Milyon elemanlık bir sıralamayı setTimeout içine koymak onu hızlandırmaz, yalnız başlangıcını erteler. Hesap yükünü gerçekten dağıtmak için ayrı bir iş parçacığı gerekir; çalışma zamanları bunu ayrı bir yürütme bağlamı olarak sunar ve bağlamlar arasında yalnız ileti geçişi yapılır — paylaşılan değişken yoktur.

Kursun Örneği: Ölçüm Akışı

Kurs boyunca tek bir somut örnek üzerinde ilerlenecek: birkaç ölçüm istasyonundan sıcaklık değeri toplayan küçük bir program. İstasyonların yanıt gecikmeleri farklıdır; bazıları hata döndürür, bazıları hiç yanıt vermez.

Bu örnek ders ders şu adımlardan geçecek: geri çağrıyla tek ölçüm, sözle zincirlenmiş ölçüm, birleştiricilerle toplu ölçüm, zaman aşımıyla sınırlanmış ölçüm, iptal edilebilir ölçüm ve son olarak sızıntı bırakmayan sürekli ölçüm akışı.

Program boyunca yazdırılan hiçbir şey süre olmayacak; yalnız sıra olacak. Süre makineye ve yüke göre değişir, sıra ise modelin kuralıdır.

Özet

  • JavaScript kullanıcı kodu tek bir iş parçacığında yürür; iki kod parçası aynı anda ilerlemez, bu nedenle veri yarışı oluşmaz.
  • Sonuna kadar çalışma kuralı gereği, başlayan bir iş bitmeden sıradaki iş ele alınmaz; sıfır gecikmeli bir geri çağrı bile bunu bekler.
  • Engelleyen iş, iş parçacığını meşgul ederek bekleyen bütün işleri geciktirir; uzun hesap ve eşzamanlı girdi-çıktı iki kaynağıdır.
  • Zamanlayıcı, ağ ve dosya erişimi dilin değil ana ortamın yetenekleridir; dil yalnız sözleri ve mikro görev kuralını tanımlar.
  • Eşzamansızlık beklemeleri üst üste bindirir, hesabı paralelleştirmez.

Sonraki Adım

Bu derste geri çağrıların “sıraya girdiği” söylendi ama sıranın nasıl işlediği söylenmedi. Sıradaki her işin eşit olmadığı, bir söz geri çağrısının bir zamanlayıcı geri çağrısından önce çalıştığı da gösterilmedi. Sonraki ders bu düzeneği açar: çağrı yığıtı, görev kuyruğu ve mikro görev kuyruğu ayrımıyla olay döngüsünün tam kuralını kurar ve karışık bir örnekte çıktı sırasını satır satır gerekçelendirir.

İ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