Sobes.tech
Middle

Was ist ein Race Condition (Wettlaufbedingung)?

sobes.tech KI

Antwort von AI

Der Zustand eines Rennens tritt auf, wenn zwei oder mehr Threads oder Prozesse gleichzeitig auf eine gemeinsame Ressource zugreifen (z. B. eine Variable, eine Datei, eine Datenbank), und das Ergebnis der Operation vom Ablauf dieser Zugriffe abhängt. Aufgrund der unvorhersehbaren Reihenfolge des Zugriffs und der Datenänderung kann das Ergebnis falsch oder unerwartet sein.

Beispiel in iOS: Angenommen, wir haben einen Zähler count, auf den gleichzeitig zwei Threads zugreifen, um ihn zu inkrementieren.

var count = 0

func incrementCount() {
    // Thread 1 liest count = 5
    // Thread 2 liest count = 5
    let currentCount = count
    // Thread 1 berechnet newCount = 6
    // Thread 2 berechnet newCount = 6
    let newCount = currentCount + 1
    // Thread 1 schreibt count = 6
    // Thread 2 schreibt count = 6 (überschreibt das Ergebnis von Thread 1)
    count = newCount
}

// Wenn zwei Threads gleichzeitig incrementCount() aufrufen,
// sollte das Ergebnis count = 2 sein,
// aber aufgrund des Rennens kann count 1 werden.

Um Rennbedingungen zu vermeiden, werden Synchronisationsmechanismen eingesetzt:

  • Lock/Mutex: Sperrt den Zugriff auf die Ressource, solange der Thread sie benutzt.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Sperre erwerben
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Sperre freigeben
    }
    
  • Queues (Warteschlangen): Verwendung von seriellen GCD-Warteschlangen.
    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
        }
    }
    
  • Atomare Operationen: Unteilbare Operationen auf CPU-Instruktionsniveau. In Swift kann man OSAtomic... (veraltet) oder Atomic aus @_implementationOnly import Atomics (experimentell) verwenden, oder Ansätze auf Basis von Sperren/Warteschlangen.

Die Wahl der Synchronisierungsmethode hängt vom konkreten Szenario und den Leistungsanforderungen ab. Es ist wichtig zu verstehen, dass Synchronisation zusätzliche Last verursachen und zu gegenseitigen Sperren (Deadlocks) führen kann, wenn sie falsch implementiert wird.