Sobes.tech
Middle

Mi az a versenyhelyzet (race condition)?

sobes.tech MI

Válasz az MI-től

A versenyhelyzet akkor fordul elő, amikor két vagy több szál vagy folyamat egyidejűleg hozzáfér egy közös erőforráshoz (például egy változóhoz, fájlhoz, adatbázishoz), és a művelet eredménye attól függ, hogy ezek a szálak/proceseik milyen sorrendben hajtják végre a műveleteiket. A nem megjósolható hozzáférési és adatmódosítási sorrend miatt az eredmény helytelen vagy váratlan lehet.

Példa iOS-en: Tegyük fel, hogy van egy count számláló, amelyhez egyszerre két szál fér hozzá, hogy növelje.

var count = 0

func incrementCount() {
    // Az 1. szál olvassa a count = 5 értéket
    // A 2. szál olvassa a count = 5 értéket
    let currentCount = count
    // Az 1. szál kiszámítja az új értéket = 6
    // A 2. szál kiszámítja az új értéket = 6
    let newCount = currentCount + 1
    // Az 1. szál írja a count = 6 értéket
    // A 2. szál írja a count = 6 értéket (felülírva az 1. szál eredményét)
    count = newCount
}

// Ha két szál egyidejűleg hívja az incrementCount() függvényt,
// a várt eredmény count = 2 lenne,
// de a versenyhelyzet miatt a count 1 lehet.

A versenyhelyzetek elkerülése érdekében szinkronizációs mechanizmusokat alkalmaznak:

  • Lock/Mu: Blokkolja az erőforráshoz való hozzáférést, amíg a szál használja.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Zárolás megszerzése
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Zárolás feloldása
    }
    
  • Sorok (Queues): GCD szekvenciális sorok használata.
    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
        }
    }
    
  • Atomi műveletek: Oszlop szintű, megszakítás nélküli műveletek. Swiftben használható az OSAtomic... (elavult) vagy az Atomic az @_implementationOnly import Atomics-ból (kísérleti), vagy zárolások/sorok alapú megközelítések.

A szinkronizációs módszer kiválasztása a konkrét szcenáriótól és a teljesítménykövetelményektől függ. Fontos megérteni, hogy a szinkronizáció plusz terhelést okozhat, és helytelen implementáció esetén kölcsönös zárolásokhoz (deadlock) vezethet.