Bölüm 6: Senkronizasyon
Çıktı Sorusu · Giriş
Soru 1: Kayıp Güncelleme
Paylaşımlı sayac = 5. T1 sayac = sayac + 1, T2 sayac = sayac + 2 çalıştırıyor; her biri üç makine komutuna derleniyor. Sıra: T1 LOAD, T2 LOAD, T2 ADD, T2 STORE, T1 ADD, T1 STORE. Son değer nedir? Tüm olası son değerler nelerdir?
/* T1 */ /* T2 */ LOAD R1, sayac LOAD R2, sayac ADD R1, 1 ADD R2, 2 STORE sayac, R1 STORE sayac, R2
Cevabı göster
Son değer 6: T1 eski değeri (5) yazmacına okuduğu için T2'nin yazdığı 7'nin üzerine 6 yazar; T2'nin +2'si kaybolur.
20 olası serpiştirmenin sonuçları yalnızca 6, 7 ve 8'dir: sıralı çalışırlarsa 8, T1'in güncellemesi kaybolursa 7, T2'ninki kaybolursa 6. Sonucun yürütme sırasına bağlı olması yarış durumudur; çözüm, oku–artır–yaz üçlüsünü kritik bölge yapmak ya da atomik artırma kullanmaktır.
Çoktan Seçmeli · Kritik Bölge
Soru 2: Katı Sırayla Değişim
İki süreç girişte while (turn != i) ; ile bekliyor, çıkarken turn = j yapıyor. P1 uzun bir kritik dışı işe dalmışken kritik bölge boş olduğu hâlde P0 ikinci kez giremiyor. Hangi koşul bozulur?
B: Kritik bölge dışındaki P1, P0'ın girişini engelliyor; giriş kararı P1 bir daha gelene kadar ertelenir. Karşılıklı dışlama sağlanır (turn aynı anda iki değer alamaz); bekleme de sınırlıdır, ama süreçler zorla dönüşümlü çalışır.
Adım Adım Çalıştırma · Yazılım Yaklaşımları
Soru 3: Peterson İzleme
P0 ve P1 (i kendi numarası, j = 1 − i) aynı anda girmek istiyor. Sıra: P0 1. satır, P1 1. satır, P1 2. satır, P0 2. satır; sonra ikisi de koşulu sınıyor. Kritik bölgeye hangisi girer?
Başlangıç: iki bayrak da 0, kimse girmek istemiyor. Sıra sıralı tutarlı bellek varsayımıyla izleniyor.
P0 niyetini bildirir: flag[0] = 1.
P1 de niyetini bildirir: flag[1] = 1.
P1 sırayı P0'a verir: turn = 0.
P0 sırayı P1'e verir: turn = 1. turn'e son yazan P0'dır; 0 değeri kayboldu.
P0: flag[1] = 1 ve turn == 1 → koşul doğru, P0 meşgul bekler.
P1: turn == 0 yanlış → döngüden çıkar, kritik bölgeye P1 girer.
P1 çıkarken flag[1] = 0 yapar; P0'ın koşulu yanlışlanır ve P0 girer. Sırayı karşıya son veren bekler.
flag[i] = true; /* girmek istiyorum */ turn = j; /* önce sen buyur */ while (flag[j] && turn == j) ; /* meşgul bekle */ /* kritik bölge */ flag[i] = false;
Çıktı Sorusu · Bellek Modeli
Soru 4: Yazma Tamponu Testi
Başlangıçta x = y = 0. Sıralı tutarlılıkta (SC) hangi (r1, r2) sonuçları mümkündür? x86 (TSO) üzerinde hangi sonuç ek olarak gözlenebilir, neden?
/* T1 */ /* T2 */ x = 1; y = 1; r1 = y; r2 = x;
Cevabı göster
SC: (0, 1), (1, 0) ve (1, 1). Altı serpiştirmenin hiçbiri (0, 0) vermez: her iş parçacığında yazma okumadan önce geldiği için ilk okuma yapıldığında en az bir yazma bitmiştir.
TSO: (0, 0) de gözlenebilir. Her çekirdeğin yazması önce kendi yazma tamponuna gider; sonraki okuma bellekten eski değeri alır. x86 yalnızca “yazma → sonraki okuma” yeniden sıralamasına izin verir; ARM ve RISC-V daha fazlasına.
Çare: yazma ile okuma arasına tam bellek bariyeri (x86 mfence, C11 atomic_thread_fence) ya da sıralı tutarlı atomikler. Peterson algoritmasını bozan da aynı yeniden sıralamadır.
Çoktan Seçmeli · Bellek Modeli
Soru 5: Peterson'u Düzeltmek
C ile yazılmış Peterson algoritması x86 üzerinde ara sıra iki iş parçacığını birlikte kritik bölgeye sokuyor. Hangi değişiklik bunu düzeltir?
D: Yazma, tamponda beklerken sonraki okuma bellekten eski değeri alıyor; tam bariyer (ya da seq_cst atomikler) bu yeniden sıralamayı yasaklar. C'deki volatile ne atomiklik ne sıralama sağlar; uyku yalnızca zamanlamayı değiştirir. C algoritmanın kendisini bozar: önce turn yazılırsa iki süreç de girebilir.
Adım Adım Çalıştırma · Donanım Desteği
Soru 6: CAS ile Artırma
v = 10 iken T1 ve T2 artir(&v) çağırıyor. Sıra: T1 okur, T2 okur, T1 CAS, T2 CAS, ardından T2 yeniden dener. Son değer nedir, toplam kaç CAS çağrısı yapılır?
compare_and_swap(v, beklenen, yeni) tek atomik komuttur: *v == beklenen ise yeni değeri yazar, her durumda eski değeri döndürür.
T1 değeri okur: temp = 10. Kilit olmadığı için burada kesilebilir.
T2 de 10 okur. Düz v++ olsaydı burada bir artış kaybolacaktı.
T1'in CAS'ı v'yi hâlâ 10 bulur, 11 yazar ve 10 döndürür: temp'e eşit → döngüden çıkar.
T2'nin CAS'ı v'yi 11 bulur: beklenen 10 değil, hiçbir şey yazmaz ve 11 döndürür → döngü tekrarlanır.
T2 güncel değeri okur: temp = 11.
Son değer 12, toplam 3 CAS. Hiçbir artış kaybolmadı ve kimse kilit tutmadı (kilitsiz güncelleme).
void artir(int *v) { int temp; do { temp = *v; } while (compare_and_swap(v, temp, temp + 1) != temp); }
Çoktan Seçmeli · Donanım Desteği
Soru 7: TSL Spinlock'unun Eksiği
while (test_and_set(&lock)) ; ile girilen, lock = false; ile çıkılan basit spinlock kritik bölge çözümünün hangi koşulunu garanti etmez?
A: Kilit boşaldığında hangi iş parçacığının test_and_set'i önce çalışacağı belirsizdir; şanssız bir iş parçacığı hep kaybedebilir (açlık). Atomik komut dışlamayı, boş kilidin hemen alınabilmesi ilerlemeyi sağlar. Sınırlı bekleme için çıkışta sırayı waiting[] dizisiyle devreden sürüm gerekir.
Çıktı Sorusu · Kilitler
Soru 8: Yaklaşık Sayaç
Dört çekirdekli makinede çekirdek başına sayaç kullanılıyor, eşik S = 4. Sırayla çekirdek 0 altı, çekirdek 1 üç, çekirdek 2 dokuz, çekirdek 3 iki kez ekle() çağırıyor. Sonunda oku() (yalnızca G'yi döndürür) kaçı verir? Gerçek değer kaçtır? En kötü durumda fark en fazla kaç olabilir?
void ekle(int c) { lock(&yerel_kilit[c]); yerel[c]++; if (yerel[c] >= S) { lock(&genel_kilit); G += yerel[c]; unlock(&genel_kilit); yerel[c] = 0; } unlock(&yerel_kilit[c]); }
Cevabı göster
| Çekirdek | Artış | G'ye aktarılan | Yerel kalan |
|---|---|---|---|
| 0 | 6 | 4 | 2 |
| 1 | 3 | 0 | 3 |
| 2 | 9 | 4 + 4 | 1 |
| 3 | 2 | 0 | 2 |
oku() = G = 12; gerçek değer 20 (fark 8, yerel sayaçlarda bekliyor).
En kötü durumda her yerel sayaç S − 1'de durur: 4 × 3 = 12. Büyük S genel kilide seyrek gider, iyi ölçeklenir ama okunan değer daha geride kalır.
Çoktan Seçmeli · Semaforlar
Soru 9: Bağlantı Havuzu
Bir veritabanına aynı anda en fazla 5 bağlantı açılmasına izin verilecek. Her iş parçacığı bağlanmadan önce wait(S), bağlantıyı kapatınca signal(S) çağırıyor. S'nin başlangıç değeri ne olmalıdır?
D: Sayan semaforun başlangıç değeri aynı anda kullanılabilecek kaynak sayısıdır: 5 iş parçacığı geçer, altıncı bloke olur. Başlangıç 1 karşılıklı dışlama kalıbıdır (yalnız bir bağlantı); 0 ise sıralama kalıbıdır, ilk signal gelene kadar kimse geçemez.
Adım Adım Çalıştırma · Semaforlar
Soru 10: Semafor Kuyruğu İzleme
S = 2 ile başlayan sayan semaforda (Silberschatz gerçekleştirmesi, FIFO kuyruk) sırayla: P1 wait, P2 wait, P3 wait, P1 signal, P4 wait, P5 wait, P2 signal, P3 signal. Sonda S kaçtır, kimler kaynakta, kimler bekliyor?
Sayan semafor 2 ile başlar: iki özdeş kaynak boşta. Değer negatifse mutlak değeri bekleyen sayısıdır.
P1 wait: 2 → 1, negatif değil → P1 kaynağı alır.
P2 wait: 1 → 0 → P2 de geçer. İki kaynak da dolu.
P3 wait: 0 → −1 < 0 → P3 kuyruğa girer ve uyur.
P1 signal: −1 → 0, hâlâ ≤ 0 → kuyruğun başındaki P3 uyandırılır ve kaynağı alır.
P4 wait: 0 → −1 → P4 kuyruğa girer.
P5 wait: −1 → −2 → P5 de kuyruğa girer. |−2| = 2 süreç bekliyor.
P2 signal: −2 → −1 ≤ 0 → P4 uyanır.
P3 signal: −1 → 0 ≤ 0 → P5 uyanır. Son durum: S = 0, kaynakta P4 ve P5, kuyruk boş.
wait(S) {
S->deger--;
if (S->deger < 0) {
kuyruğa ekle; block(); }
}
signal(S) {
S->deger++;
if (S->deger <= 0) {
P = kuyruktan çıkar; wakeup(P); }
}Çıktı Sorusu · Semaforlar
Soru 11: Olası Çıktılar
x ve y semaforları 0 ile başlıyor; iki iş parçacığı aynı anda başlatılıyor. Ekrana yazılabilecek tüm çıktılar nelerdir?
/* T1 */ printf("A"); signal(&x); printf("B"); wait(&y); printf("C"); /* T2 */ wait(&x); printf("D"); signal(&y); printf("E");
Cevabı göster
Semaforların zorladığı sıralar: A → D (D, A'dan sonraki signal(x)'i bekler), D → C (C, D'den sonraki signal(y)'yi bekler); ayrıca her iş parçacığının kendi sırası: A → B → C ve D → E.
Bu kurallara uyan 5 çıktı vardır: ABDCE, ABDEC, ADBCE, ADBEC, ADEBC. A her zaman ilk, C her zaman D'den sonradır; B ile D ve C ile E'nin sırası çizelgeleyiciye kalır.
0 ile başlayan semafor bir “olay oldu” işaretidir: signal önce gelirse değer 1 olur, sonraki wait beklemeden geçer.
Çoktan Seçmeli · Klasik Problemler
Soru 12: Ters Sıralı down
Sınırlı tamponlu üretici-tüketici çözümünde üretici yanlışlıkla önce down(&mutex), sonra down(&empty) yapıyor; tüketici doğru sırada. Tampon tamamen doluyken üretici çalışırsa ne olur?
C: Üretici mutex'i alır, empty = 0 olduğu için down(&empty)'de mutex elindeyken uyur. Tüketici down(&full)'ü geçer ama down(&mutex)'te uyur; up(&empty) yapacak olan oydu. Kural: önce sayma semaforunu, sonra mutex'i bekle; mutex'i tutarken uyuma.
Çoktan Seçmeli · Klasik Problemler
Soru 13: Yedi Filozof
Yuvarlak masada 7 filozof ve aralarında 7 çatal var; her filozof yemek için sağındaki ve solundaki iki çatala ihtiyaç duyuyor. Aynı anda en fazla kaç filozof yemek yiyebilir?
B: Yiyen her filozof 2 çatal tutar ve yan yana iki filozof aynı anda yiyemez: ⌊7 / 2⌋ = 3 (7 çatalın 6'sı kullanılır). C, 7 / 2'yi yukarı yuvarlar; dördüncü filozofun iki komşusundan biri mutlaka yiyordur.
Çoktan Seçmeli · Monitörler
Soru 14: Neden while?
Pthreads ile yazılan tüketicide if (count == 0) pthread_cond_wait(&c, &m); satırı var. Bu kod neden hatalıdır?
A: signal yalnızca bir ipucudur; uyanan iş parçacığı mutex'i yeniden alana kadar başka bir tüketici öğeyi almış olabilir (barging). POSIX sahte uyanmaya da izin verir. B yanlıştır: cond_wait mutex'i atomik olarak bırakıp uyur. C yanlıştır: koşul değişkeninin değeri yoktur, bekleyen yokken gelen sinyal kaybolur.
Çıktı Sorusu · POSIX ve Java
Soru 15: Kilit ve Semafor Sayaçları
Aşağıdaki Java programının çıktısı nedir?
ReentrantLock k = new ReentrantLock(); k.lock(); k.lock(); System.out.print(k.getHoldCount() + " "); k.unlock(); System.out.println(k.isLocked()); k.unlock(); Semaphore s = new Semaphore(2); s.acquire(); s.acquire(); System.out.print(s.tryAcquire() + " "); s.release(3); System.out.println(s.availablePermits() + " " + k.isLocked());
Cevabı göster
2 true false 3 false
ReentrantLockyeniden girilebilir: sahibi iki kez kilitleyince tutma sayısı 2 olur. Bir unlock sonrası sayı 1, kilit hâlâ tutuluyor:true. İkinci unlock kilidi bırakır.- İzin sayısı 2 olan semafordan iki acquire sonra izin kalmaz:
tryAcquire()beklemedenfalsedöner. release(3)değeri 3 artırır; Java semaforu başlangıç değerini üst sınır saymaz:3. SonisLocked()false.
Çıktı Sorusu · Canlılık
Soru 16: Öncelik Terslemesi Hesabı
Tek CPU, kesmeli öncelik (H > M > L). L t = 0'da kilidi alır, kritik bölgesi 4 birim sürer. H t = 1'de gelir, hemen aynı kilidi ister; kilidi alınca 2 birim çalışır. M t = 2'de gelir, 5 birim çalışır, kilit kullanmaz. Öncelik kalıtımı yokken ve varken H kilit için kaç birim bekler, ne zaman biter?
Cevabı göster
Kalıtım yokken H'yi kilitle hiç ilgisi olmayan M bekletir: öncelik terslemesi. M uzadıkça bekleme sınırsız uzar. Kalıtımla L, H bloke olduğu andan itibaren H'nin önceliğiyle çalışır; M araya giremez, L kilidi 4'te bırakır.
Çoktan Seçmeli · Canlılık
Soru 17: Kibar İş Parçacıkları
İki iş parçacığı ikişer kilit istiyor. İkinci kilidi trylock ile alamayan, elindekini bırakıp hep aynı süre sonra yeniden deniyor. İkisi de sürekli çalışıyor ama hiçbiri işini bitiremiyor. Bu durumun adı ve doğru tanımı hangisidir?
C: Canlı kilitte süreçler bloke değildir; durum değiştirip dururlar ama ilerleme yoktur. Çare: rastgele (üstel) geri çekilme ya da kilit sıralaması. Kilitlenmede iş parçacıkları uyur; açlıkta en az biri ilerler.
Çoktan Seçmeli · Kilitsiz Programlama
Soru 18: ABA Sorunu
Kilitsiz yığında T1 pop için top = A ve A.next = B okuyor, sonra kesiliyor. Bu arada T2 A'yı çıkarıp B'yi çıkarıyor ve A'yı yeniden ekliyor. T1'in CAS(top, A, B) çağrısı başarılı olur ama yığın bozulur. Sorunun adı ve çaresi nedir?
A: CAS yalnızca değerin eşitliğine bakar; top'un A → B → A değiştiğini fark etmez ve artık yığında olmayan B'yi top yapar. Çare: her değişiklikte artan sürüm sayacıyla birlikte karşılaştırmak ya da serbest bırakılan düğümü güvenli geri kazanımla (hazard pointer, epoch) yeniden kullanmamak.