Sobes.tech
Middle+

Hogyan oldható meg a versenyhelyzet (race condition) problémája?

sobes.tech MI

Válasz az MI-től

Szálak szinkronizálásának használata a közös erőforrásokhoz való hozzáféréshez.

Fő megközelítések:

  • Mutexek (Zárak): Blokkolják a hozzáférést az erőforráshoz más szálak számára, amíg egy szál használja.
    • NSLock
    • os_unfair_lock
    • pthread_mutex_t
import Foundation

let lock = NSLock()
var sharedResource = 0

func modifyResource() {
    lock.lock() // Zár megszerzése
    // Kritikus szekció - biztonságos hozzáférés a sharedResource-hoz
    sharedResource += 1
    print("Erőforrás értéke: \(sharedResource)")
    lock.unlock() // Zár feloldása
}

// Példa hívás külön szálakról
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
  • Sorozatba rendezett sorok (Serial Queues): Feladatokat egymás után hajtanak végre, garantálva, hogy egyszerre csak egy kódrészlet fér hozzá az erőforráshoz.
    • DispatchQueue.main
    • DispatchQueue.global() .serial attribútummal
import Foundation

let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0

func modifyResourceAsync() {
    serialQueue.async {
        // A kód sorban végrehajtódik
        sharedResource += 1
        print("Erőforrás értéke: \(sharedResource)")
    }
}

// Példa hívás külön szálakról/növekményekről
// modifyResourceAsync()
// modifyResourceAsync()
  • Olvasó-Író Zárak (Reader-Writer Locks): Lehetővé teszik, hogy több szál olvashasson egyszerre, de csak egy írhat.
    • DispatchQueue barrier-ekkel (.barrier) az íráshoz, és szinkron/aszinkron hozzáféréssel az olvasáshoz.
import Foundation

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0

func readResource() {
    concurrentQueue.async { // Olvasás - több olvasó egyszerre
        print("Erőforrás olvasása: \(sharedResource)")
    }
}

func writeResource(newValue: Int) {
    concurrentQueue.sync(flags: .barrier) { // Írás - csak egy író, és senki sem olvas vagy ír
        sharedResource = newValue
        print("Erőforrás írása: \(sharedResource)")
    }
}

// Példa hívások
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
  • Atomi műveletek (Atomic Operations): Olyan műveletek, amelyek garantáltan teljes egészében végrehajtódnak megszakítás nélkül más szál által. Egyszerű típusú adatokhoz (számlálók, zászlók) használják.
    • OSAtomicIncrement, OSAtomicDecrement (Elavult, de a koncepció releváns)
    • C++11 <atomic>
#include <atomic>
#include <iostream>

std::atomic<int> atomicCounter(0);

void incrementCounter() {
    atomicCounter++; // Atomi növelés
    std::cout << "Számláló: " << atomicCounter << std::endl;
}

// C++ példakód iOS környezetben (Objective-C++ használatával)
// Külön szálakról hívható
  • Értéktípusok (Value Types): Struktúrák vagy enumerációk másolatainak átadása segíthet elkerülni az adatok versenyhelyzetét, ha az adatok nem osztoznak közvetlenül a szálak között módosítható formában.

A módszer kiválasztása a feladat jellemzőitől és a kívánt párhuzamossági szinttől függ. Az iOS-en a legtöbb feladathoz a DispatchQueue (Serial és Concurrent barrier-ekkel) ajánlott, mivel könnyen használható és jól integrálódik a Grand Central Dispatch (GCD) rendszerrel.