Middle+
Wie löst man das Problem des Race Conditions?
sobes.tech KI
Antwort von AI
Verwenden Sie die Thread-Synchronisation, um auf gemeinsame Ressourcen zuzugreifen.
Hauptansätze:
- Mutexe (Mutual Exclusion): Sperren den Zugriff auf eine Ressource für andere Threads, während ein Thread sie verwendet.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Mutex sperren
// Kritischer Abschnitt - sicherer Zugriff auf sharedResource
sharedResource += 1
print("Ressourcenwert: \(sharedResource)")
lock.unlock() // Mutex freigeben
}
// Beispiel für Aufrufe aus verschiedenen Threads
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Serielle Warteschlangen: Führen Aufgaben nacheinander aus, garantieren, dass nur ein Codeblock gleichzeitig Zugriff auf die Ressource hat.
DispatchQueue.mainDispatchQueue.global()mit Attribut.serial
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Code wird sequenziell in dieser Warteschlange ausgeführt
sharedResource += 1
print("Ressourcenwert: \(sharedResource)")
}
}
// Beispiel für Aufrufe aus verschiedenen Threads/Warteschlangen
// modifyResourceAsync()
// modifyResourceAsync()
- Leser-Schreiber-Sperren: Erlauben mehreren Threads, gleichzeitig zu lesen, aber nur einem zu schreiben.
DispatchQueuemit Barrieren (.barrier) für Schreibzugriffe und synchronem/asynchronem Lesezugriff.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Lesen - mehrere Leser gleichzeitig
print("Lesen der Ressource: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Schreiben - nur ein Schreiber, keine Leser oder andere Schreiber
sharedResource = newValue
print("Schreiben der Ressource: \(sharedResource)")
}
}
// Beispiel für Aufrufe
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomare Operationen: Garantierte vollständige Ausführung ohne Unterbrechung durch andere Threads. Für einfache Datentypen (Zähler, Flags) verwendet.
OSAtomicIncrement,OSAtomicDecrement(veraltet, aber das Konzept ist relevant)<atomic>in C++11
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Atomarer Inkrement
std::cout << "Zähler: " << atomicCounter << std::endl;
}
// Beispiel im iOS-Kontext mit C++ (Verwendung mit Objective-C++)
// Aus verschiedenen Threads aufrufen
- Werttypen: Das Übergeben von Kopien von Strukturen/Enums anstelle von Referenzen auf Klassen kann helfen, Race-Conditions zu vermeiden, wenn die Daten nicht direkt zwischen Threads in änderbarer Form geteilt werden.
Die Wahl der Methode hängt von der Art der Aufgabe und dem erforderlichen Parallelitätsgrad ab. Für die meisten Aufgaben in iOS sind DispatchQueue (seriell und parallel mit Barrieren) aufgrund der einfachen Handhabung und Integration mit Grand Central Dispatch (GCD) vorzuziehen.