Sobes.tech
Middle+

Hoe los je het probleem van raceconditie op?

sobes.tech AI

Antwoord van AI

Gebruik thread-synchronisatie voor toegang tot gedeelde bronnen.

Belangrijkste benaderingen:

  • Mutexen (Mutual Exclusion): Blokkeert toegang tot een bron voor andere threads terwijl één deze gebruikt.
    • NSLock
    • os_unfair_lock
    • pthread_mutex_t
import Foundation

let lock = NSLock()
var sharedResource = 0

func modifyResource() {
    lock.lock() // Mutex verkrijgen
    // Kritieke sectie - veilige toegang tot sharedResource
    sharedResource += 1
    print("Resourcewaarde: \(sharedResource)")
    lock.unlock() // Mutex vrijgeven
}

// Voorbeeld van aanroepen vanuit verschillende threads
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
  • Seriële wachtrijen: Voeren taken één voor één uit, waardoor gegarandeerd wordt dat slechts één codeblok toegang heeft tot de bron tegelijk.
    • DispatchQueue.main
    • DispatchQueue.global() met .serial attribuut
import Foundation

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

func modifyResourceAsync() {
    serialQueue.async {
        // Code wordt sequentieel uitgevoerd in deze wachtrij
        sharedResource += 1
        print("Resourcewaarde: \(sharedResource)")
    }
}

// Voorbeeld van aanroepen vanuit verschillende threads/wachtrijen
// modifyResourceAsync()
// modifyResourceAsync()
  • Lezer-schrijf vergrendelingen: Mogen meerdere threads tegelijk lezen, maar slechts één schrijven.
    • DispatchQueue met .barrier barrières voor schrijven en synchronisatie/asynchrone toegang voor lezen.
import Foundation

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

func readResource() {
    concurrentQueue.async { // Lezen - meerdere lezers tegelijk
        print("Lezen van resource: \(sharedResource)")
    }
}

func writeResource(newValue: Int) {
    concurrentQueue.sync(flags: .barrier) { // Schrijven - slechts één schrijver en niemand leest/schrijft
        sharedResource = newValue
        print("Schrijven naar resource: \(sharedResource)")
    }
}

// Voorbeeld van aanroepen
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
  • Atomische operaties: Operaties die gegarandeerd volledig worden uitgevoerd zonder onderbreking door andere threads. Gebruikt voor eenvoudige datatypes (telers, flags).
    • OSAtomicIncrement, OSAtomicDecrement (Verouderd, maar concept blijft relevant)
    • <atomic> in C++11
#include <atomic>
#include <iostream>

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

void incrementCounter() {
    atomicCounter++; // Atomische increment
    std::cout << "Counter: " << atomicCounter << std::endl;
}

// Voorbeeld in iOS-context met C++ (gebruik met Objective-C++)
// Aanroepen vanuit verschillende threads
  • Value types: Het doorgeven van kopieën van structuren/enums in plaats van referenties naar klassen kan helpen om racecondities te voorkomen, als de gegevens niet direct tussen threads worden gedeeld in wijzigbare vorm.

De keuze van methode hangt af van de aard van de taak en het vereiste niveau van parallelisme. Voor de meeste taken in iOS zijn DispatchQueue (serieel en gelijktijdig met barrières) de voorkeur vanwege gebruiksgemak en integratie met Grand Central Dispatch (GCD).