İçeriğe geç

Eşzamanlılık (Thread'ler & Mutex'ler)

Tulpar OS thread’lerini doğrudan üç yerleşik fonksiyon üzerinden sunar: thread_create, mutex_create ve mutex_lock / mutex_unlock çifti. Bunlar gerçek OS thread’leridir — her thread_create çağrısı bir tane başlatır, dolayısıyla kilitlerle iş birliği sizin sorumluluğunuzdadır. Kooperatif, tek-thread’li eşzamanlılık (bloklamayan timer ve HTTP) için bunun yerine Async / Await’e bakın.

thread_create(handler_adi, arg) ilk argümanın adıyla belirtilen fonksiyonu çağıran yeni bir thread başlatır ve ikinci argümanı tek bir değer olarak ona iletir. Dönüş bir thread id’sidir (int); join etmeyecekseniz thread_detach(id) ile eşleştirin.

func isci(arg) {
print("Isçi çalışıyor, arg =", arg);
}
int t = thread_create("isci", 42);
thread_detach(t);

Handler ilk çalıştığında ada (string) göre aranır ve önbelleğe alınır, dolayısıyla AOT pipeline’ının dışa aktardığı bir üst seviye fonksiyon olmalıdır (her func tanımı için varsayılan).

Birden fazla thread’in mutasyona uğrattığı bir sayaç klasik yarış durumudur. Oku-değiştir-yaz bölgesini mutex_lock / mutex_unlock ile sarın:

int sayac = 0;
int mu = mutex_create();
func arttir(arg) {
mutex_lock(mu);
sayac = sayac + 1;
mutex_unlock(mu);
}
for (int i = 0; i < 100; i++) {
int t = thread_create("arttir", i);
thread_detach(t);
}
sleep(100); // thread'lerin bitmesi için biraz zaman ver
print("sayac =", sayac);

Kilidi tutabildiğiniz en kısa bölge için tutun — tipik olarak sadece paylaşılan duruma yazma. lib/wings.tpr içindeki HTTP sunucusu tam bu deseni kullanır: handler dispatch çevresinde kilitler, response inşa edilip gönderilmeden önce kilidi açar.

Çalışan örnek: thread’li HTTP sunucusu

Section titled “Çalışan örnek: thread’li HTTP sunucusu”

lib/wings.tpr (Wings framework’u) ana döngüsünde yeni TCP bağlantılarını kabul eder ve her bağlantıyı bir worker thread’ine verir. Çekirdek deyim şöyle görünür:

import "wings";
func handle_root() {
return wings_text("merhaba thread " + toString(thread_id()));
}
get("/", "handle_root");
listen(8080); // wings dahili olarak bağlantı başına thread başlatır

Tüm framework için HTTP Sunucusu (Wings) sayfasına bakın — thread’leme listen içinde gerçekleşir, yaygın durum için bunu kendiniz bağlamak zorunda değilsiniz.

  • Küçük değer iletin. thread_create tek arg alır. Daha fazla bağlam için bir arrayJson içine paketleyip worker’da çıkartın.
  • Sadece paylaşılan yazma çevresinde kilit. Değişmez verilerin okunması kilit gerektirmez; tek global kilit yerine kaynak başına mutex kullanın.
  • Join etmiyorsanız detach edin. Sızdırılmış thread handle’ı asla geri alınmayacak gerçek bir OS thread’idir.
  • Sleep senkronizasyon değildir. sleep(ms) koşul beklemek için değil, hız ayarlamak içindir. Bir koşul beklemek için mutex + worker’ın set ettiği bir bayrak kullanın.

Async / await (kooperatif coroutine’ler)

Section titled “Async / await (kooperatif coroutine’ler)”

OS thread’lerinden ayrı olarak Tulpar, tek bir iş parçacığında kooperatif eşzamanlılık için async / await sunar (v3.0.0’da eklendi). Bir async func hemen bir promise döndürür; await ise o promise yerleşene kadar çağıranı askıya alır ve bu sırada diğer coroutine’lerin çalışmasına izin verir.

async func yavas_iki_kat(int n) {
await sleep_async(20); // bloklamayan duraklama — diğer işler çalışır
return n * 2;
}
async func selamla(str kim) {
await sleep_async(10);
print("merhaba " + kim);
return 1;
}
// İki coroutine başlat; biz await ederken eşzamanlı çalışırlar.
var a = yavas_iki_kat(21);
var g = selamla("dunya");
int sonuc = await a; // 42
await g;
print("bitti");

yavas_iki_kat ilk başlatılıp ilk await edilmesine rağmen selamla (10 ms timer) ondan (20 ms) önce biter — iki coroutine’in bloklamak yerine iç içe geçtiğinin kanıtı.

  • async func bir coroutine’e derlenir. Çağırmak task’ı başlatır ve bekleyen bir promise döndürür; gövde, event loop onu sürene kadar (bir sonraki await’te ya da program sonunda) çalışmaz.
  • await ifadeifade bir promise ise yerleşene dek askıya alır ve değerini verir; coroutine içindeyse kontrolü scheduler’a, ana akıştaysa loop’u sürerek hazır olana dek bekler. Promise olmayan bir değerde await birim işlevdir (await 5 == 5).
  • sleep_async(ms) ms milisaniye sonra bloklamadan yerleşen bir promise döndürür — bloklayan sleep(ms)’in aksine. Bloklamayan gecikme için await sleep_async(ms) kullanın.
  • Runtime program sonunda bekleyen tüm task’ları drene eder; başlatılıp await edilmemiş bir coroutine bile tamamlanır (Node tarzı).

Bir coroutine başka bir coroutine’i await edebilir; böylece async fonksiyonları besteleyebilirsin:

async func artir(int x) { await sleep_async(2); return x + 1; }
async func zincir(int x) {
int a = await artir(x);
int b = await artir(a);
return b;
}
print(await zincir(40)); // 42