Middle+
Ako vyriešiť problém závodného stavu (race condition)?
sobes.tech AI
Odpoveď od AI
Použitie synchronizácie vlákien na prístup k spoločným zdrojom.
Hlavné prístupy:
- Mutexy (zámky): Blokujú prístup ku zdroju pre iné vlákna, kým jedno vlákno zdroj používa.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Získanie muteksa
// Kritická sekcia - bezpečný prístup k sharedResource
sharedResource += 1
print("Hodnota zdroja: \(sharedResource)")
lock.unlock() // Uvoľnenie muteksa
}
// Príklad volania z rôznych vlákien
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Sériové fronty (Serial Queues): Vykonávajú úlohy jedna po druhej, zabezpečujúc, že iba jeden blok kódu má súčasne prístup ku zdroju.
DispatchQueue.mainDispatchQueue.global()s atribútom.serial
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Kód sa vykonáva sekvenčne
sharedResource += 1
print("Hodnota zdroja: \(sharedResource)")
}
}
// Príklad volania z rôznych vlákien/front
// modifyResourceAsync()
// modifyResourceAsync()
- Čitateľ-Writer Locks (zámky čitateľ-písač): Umožňujú viacerým vláknam čítať súčasne, ale len jednému písať.
DispatchQueues bariérami (.barrier) pre zápis a synchronizovaným/asynchrónnym prístupom pre čítanie.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Čítanie - viacerí čitatelia súčasne
print("Čítanie zdroja: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Zápis - len jeden pisateľ a nikto iný nečíta/nepíše
sharedResource = newValue
print("Písanie do zdroja: \(sharedResource)")
}
}
// Príklad volania
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomické operácie: Operácie, ktoré sú garantovane vykonané celé bez prerušenia iným vláknom. Používajú sa pre jednoduché typy dát (čítače, flagy).
OSAtomicIncrement,OSAtomicDecrement(zastaralé, ale koncept je stále aktuálny)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Atomické inkrementovanie
std::cout << "Čítač: " << atomicCounter << std::endl;
}
// Príklad C++ v kontexte iOS (použitie s Objective-C++)
// Volajte z rôznych vlákien
- Typy hodnôt (Value Types): Prenos kópií štruktúr alebo enumerácií môže pomôcť vyhnúť sa problémom s race condition, ak sa údaje nezdielajú priamo medzi vláknami v meniteľnej podobe.
Výber metódy závisí od špecifík úlohy a požadovanej úrovne paralelizmu. Pre väčšinu úloh v iOS sú vhodné DispatchQueue (sériové a paralelné s bariérami) kvôli ľahkosti použitia a integrácii s Grand Central Dispatch (GCD).