Sobes.tech
Middle

Czym jest warunek wyścigu (race condition)?

sobes.tech AI

Odpowiedź od AI

Stan wyścigu występuje, gdy dwa lub więcej wątków lub procesów jednocześnie odwołuje się do wspólnego zasobu (np. zmiennej, pliku, bazy danych), a wynik operacji zależy od kolejności, w jakiej te wątki/procesy wykonują swoje działania. Ze względu na nieprzewidywalny porządek dostępu i modyfikacji danych, wynik może być niepoprawny lub nieoczekiwany.

Przykład w iOS: Załóżmy, że mamy licznik count, do którego jednocześnie odwołują się dwa wątki, aby go zwiększyć.

var count = 0

func incrementCount() {
    // Wątek 1 odczytuje count = 5
    // Wątek 2 odczytuje count = 5
    let currentCount = count
    // Wątek 1 oblicza newCount = 6
    // Wątek 2 oblicza newCount = 6
    let newCount = currentCount + 1
    // Wątek 1 zapisuje count = 6
    // Wątek 2 zapisuje count = 6 (nadpisując wynik Wątku 1)
    count = newCount
}

// Jeśli dwa wątki wywołają incrementCount() jednocześnie,
// oczekiwany wynik to count = 2,
// ale ze względu na warunek wyścigu, count może wynosić 1.

Aby zapobiec warunkom wyścigu, stosuje się mechanizmy synchronizacji:

  • Lock/Mutex: Blokuje dostęp do zasobu, dopóki wątek go używa.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Zdobycie blokady
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Zwolnienie blokady
    }
    
  • Kolejki (Queues): Użycie kolejki GCD sekwencyjnych.
    import Foundation
    
    let serialQueue = DispatchQueue(label: "com.myapp.counterQueue")
    var count = 0
    
    func incrementCount() {
        serialQueue.async {
            let currentCount = count
            let newCount = currentCount + 1
            count = newCount
        }
    }
    
  • Operacje atomowe: Niepodzielne operacje na poziomie instrukcji procesora. W Swift można używać OSAtomic... (przestarzałe) lub Atomic z @_implementationOnly import Atomics (eksperymentalnie), lub podejścia oparte na blokadach/kolejkach.

Wybór metody synchronizacji zależy od konkretnego scenariusza i wymagań wydajnościowych. Ważne jest, aby rozumieć, że synchronizacja może wprowadzać dodatkowe obciążenie i potencjalnie prowadzić do blokad wzajemnych (deadlocks), jeśli jest niepoprawnie zaimplementowana.