Swift dilinde, birden çok iş parçasının aynı anda aynı veriyi değiştirmeye çalıştığı durumlar, yani data race’ler, sıkça karşılaşılan sorunlardan biridir. Bu durumlar, özellikle diziler gibi thread-safe olmayan veri yapılarının kullanıldığı durumlarda ortaya çıkabilir. Bu yazımda, Swift dilinde thread safety problemlerini çözmek için kullanılan stratejilere odaklanıp bir örnek üzerinden bu stratejileri göstereceğim.
Öncelikle, aşağıdaki gibi bir veri servisi sınıfını ele alalım:
import Foundation
class DataService {
private var data = [String]()
private let dataQueue = DispatchQueue(label: "com.example.DataService", attributes: .concurrent)
func addData(_ item: String) {
dataQueue.async(flags: .barrier) {
self.data.append(item)
}
}
func removeData(at index: Int) {
dataQueue.async(flags: .barrier) {
if index < self.data.count {
self.data.remove(at: index)
}
}
}
func updateData(at index: Int, with newItem: String) {
dataQueue.async(flags: .barrier) {
if index < self.data.count {
self.data[index] = newItem
}
}
}
func listData() {
dataQueue.sync {
print("Data List:")
for item in self.data {
print("- \(item)")
}
}
}
}
Bu sınıf, DispatchQueue
ve .barrier
bayrağı kullanarak, aynı anda sadece bir işlemin veri üzerinde değişiklik yapmasını sağlayan bir thread safety mekanizması içerir. Ancak, bu konuyu daha ayrıntılı bir şekilde anlamak için, stratejilere ve kullanılan çözüme odaklanalım.
Swift dilinde, thread safety problemlerini çözmek için kullanılan bazı temel stratejiler şunlardır:
1. Concurrent Queue ve Barrier Kullanımı
Concurrent queue, aynı anda birden fazla iş parçasının çalışmasına izin verir. Barrier özelliği, belirli bir işlemin diğerlerinden bağımsız olarak çalışmasını sağlar, bu da belirli bir işlem sırasında diğer işlemlerin beklemesine neden olur. Yukarıdaki örnekte, dataQueue
adlı concurrent queue kullanılarak, async(flags: .barrier)
ile belirli işlemler arasında bir bariyer eklenmiştir. Bu sayede, aynı anda sadece bir işlem, örneğin addData
, removeData
, veya updateData
, bu bariyeri geçerek ilgili işlemi gerçekleştirebilir.
2. Serial Queue Kullanımı
Serial queue’lar, işlemlerin sırayla ve tek bir thread üzerinde çalışmasını sağlar. Bu da aynı anda sadece bir işlemin çalışmasına izin verir. Yukarıdaki örnekte, sadece listData
fonksiyonu, veriyi sadece okuma amaçlı olduğu için dataQueue.sync
ile çağrılmıştır. Bu, veriye güvenli bir şekilde erişimi sağlar, ancak yazma işlemlerinde async(flags: .barrier)
kullanılarak bariyer eklenmiştir.
Swift dilinde diziler gibi thread-safe olmayan veri yapılarını kullanarak güvenli bir çoklu işlem ortamı sağlamak için, yukarıda bahsedilen stratejiler kullanılabilir. Ancak, unutulmamalıdır ki Swift 5.5 ve sonrasında eklenen Actor mekanizması da bu tür durumları daha etkili bir şekilde ele alır ve kodu daha anlaşılır hale getirir.