Middle+
Jak vyřešit problém závodní podmínky (race condition)?
sobes.tech AI
Odpověď od AI
Použití synchronizace vláken pro přístup ke sdíleným zdrojům.
Hlavní přístupy:
- Mutexy (zámky): Zamezují přístup ke zdroji ostatním vláknům, dokud jedno vlákno zdroj používá.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Získání zámku
// Kritická sekce - bezpečný přístup ke sharedResource
sharedResource += 1
print("Hodnota zdroje: \(sharedResource)")
lock.unlock() // Uvolnění zámku
}
// Příklad volání z různých vláken
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Serial Queue (Sekvenční fronty): Provádějí úkoly jeden po druhém, zajišťují, že pouze jedno blok kódu má současně přístup ke zdroji.
DispatchQueue.mainDispatchQueue.global()s atributem.serial
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Kód se provádí sekvenčně
sharedResource += 1
print("Hodnota zdroje: \(sharedResource)")
}
}
// Příklad volání z různých vláken/front
// modifyResourceAsync()
// modifyResourceAsync()
- Čtenář-Writer Locks (zámky čtenář-písač): Umožňují více čtenářům číst současně, ale pouze jednomu písači zapisovat.
DispatchQueues bariérami (.barrier) pro zápis a synchronní/asynchronní přístup pro čtení.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Čtení - více čtenářů současně
print("Čtení zdroje: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Zápis - pouze jeden písač a nikdo jiný nečte/nepisuje
sharedResource = newValue
print("Zápis do zdroje: \(sharedResource)")
}
}
// Příklad volání
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomické operace (Atomic Operations): Operace, které jsou garantovaně provedeny celé bez přerušení jinou vlákny. Používají se pro jednoduché datové typy (čítače, flagy).
OSAtomicIncrement,OSAtomicDecrement(zastaralé, ale koncept je stále relevantní)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Atomické zvýšení
std::cout << "Čítač: " << atomicCounter << std::endl;
}
// Příklad v C++ v kontextu iOS (použití s Objective-C++)
// Volat z různých vláken
- Typy hodnot (Value Types): Přenos kopií struktur nebo enumerací místo odkazů na třídy může pomoci předejít problémům s race condition, pokud data nejsou sdílena přímo mezi vlákny v měnitelné podobě.
Volba metody závisí na specifikách úkolu a požadované úrovni paralelismu. Pro většinu úkolů v iOS jsou preferovány DispatchQueue (sekvenční a současné s bariérami) kvůli jednoduchosti použití a integraci s Grand Central Dispatch (GCD).