Kural
Paylaşılan duruma iş parçacığı güvenli (thread-safe) erişim sağlayın.
Birden fazla iş parçacığı tarafından senkronizasyon olmaksızın erişilen paylaşılan değişken durum, yarış koşullarına ve çalışma zamanı hatalarına yol açar.
Desteklenen diller: Python, Java, C#Giriş
Birden çok iş parçacığı senkronizasyon olmadan paylaşılan değişkenlere eriştiğinde ve bunları değiştirdiğinde yarış koşulları ortaya çıkar. Nihai değer, öngörülemeyen iş parçacığı yürütme zamanlamasına bağlıdır ve bu da veri bozulmasına, yanlış hesaplamalara veya çalışma zamanı hatalarına yol açar. İş parçacıkları eski değerleri okuyup, bunları artırıp çakışan sonuçlar yazdıkça, kilitleme olmadan birden çok iş parçacığı tarafından artırılan bir sayaç güncellemeleri kaçıracaktır.
Neden önemli?
Veri bozulması ve yanlış sonuçlar: Yarış koşulları, değerlerin tutarsız veya yanlış hale geldiği sessiz veri bozulmalarına neden olur. Hesap bakiyeleri yanlış olabilir, envanter adetleri eksiye düşebilir veya toplanan istatistikler bozulabilir. Bu hataların yeniden üretilmesi zordur çünkü tam iş parçacığı zamanlamasına bağlıdırlar.
Sistem kararsızlığı: Paylaşılan duruma senkronize olmayan erişim uygulamaların çökmesine neden olabilir. Bir iş parçacığı başka bir iş parçacığı veriyi okurken veri yapısını değiştirebilir ve bu da boş referans (null pointer) hataları veya dizi sınır dışı istisnaları gibi istisnalara yol açabilir. Üretim ortamında bunlar, yük altında aralıklı çökmeler olarak kendini gösterir.
Hata ayıklama karmaşıklığı: Yarış koşulları deterministik olmadıkları için hata ayıklaması son derece zor hatalardır. Hata tek iş parçacıklı testlerde veya düşük yük ortamlarında görünmeyebilir. Yeniden üretme, zorlanması güç olan belirli iş parçacığı araya girmelerini (interleaving) gerektirir, bu da sorunların rastgele görünüp kaybolmasına neden olur.
Kod örnekleri
❌ Kural dışı:
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
current = self.balance
# Yarış koşulu: başka bir iş parçacığı burada bakiyeyi değiştirebilir
time.sleep(0.001) # İşlem süresini simüle eder
self.balance = current + amount
def withdraw(self, amount):
if self.balance >= amount:
current = self.balance
time.sleep(0.001
self.balance = current - amount
return True
return False
Neden yanlış: deposit() veya withdraw() fonksiyonlarını aynı anda çağıran birden fazla iş parçacığı yarış koşulları oluşturur. Her biri 100$ yatıran iki iş parçacığı da bakiyeyi 0$ olarak okuyabilir, ardından ikisi de 100$ yazabilir ve sonuç olarak bakiye 200$ yerine 100$ olur.
✅ Kurallara uygun:
import threading
class BankAccount:
def __init__(self):
self.__balance = 0
self.__lock = threading.Lock()
@property
def balance(self):
with self.__lock:
return self.__balance
def deposit(self, amount):
with self.__lock:
current = self.__balance
time.sleep(0.001
self.__balance = current + amount
def withdraw(self, amount):
with self.__lock:
if self.__balance >= amount:
current = self.__balance
time.sleep(0.001
self.__balance = current - amount
return True
return False
Neden önemli: The threading.Lock() aynı anda yalnızca tek bir iş parçacığının bakiyeye erişmesini sağlar. Bir iş parçacığı kilidi elinde tuttuğunda diğerleri bekler ve eşzamanlı değişiklikleri önler. Özel __balance salt okunur (readonly) ile @property harici kodun kilit korumasını atlamasını engeller.
Sonuç
Kilitler (locks), semaforlar (semaphores) veya atomik işlemler gibi uygun senkronizasyon ilkelleriyle tüm paylaşılan değişken durumları koruyun. Mümkün olduğunda değiştirilemez (immutable) veri yapılarını veya iş parçacığı yerel depolamasını (thread-local storage) tercih edin. Senkronizasyon gerekli olduğunda, çekişmeyi azaltmak ve performansı artırmak için kritik bölümleri minimumda tutun.

