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.
NSLockos_unfair_lockpthread_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.mainDispatchQueue.global().serialattribú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.
DispatchQueuebarrier-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.