Middle+
Kaip išspręsti lenktynių sąlygų (race condition) problemą?
sobes.tech AI
Atsakymas iš AI
Naudoti srautų sinchronizaciją prieigos prie bendrų išteklių.
Pagrindiniai požiūriai:
- Mutexai (Mūteksai): Blokuoja prieigą prie ištekliaus kitiems srautams, kol vienas srautas jį naudoja.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Užfiksuojame mūteksą
// Kritinė sekcija - saugi prieiga prie sharedResource
sharedResource += 1
print("Resource value: \(sharedResource)")
lock.unlock() // Atlaisviname mūteksą
}
// Pavyzdys iš skirtingų srautų
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Srieginės eilės (Serial Queues): Atlieka užduotis po vieną, garantuodamos, kad vienu metu tik vienas kodo blokas turi prieigą prie ištekliaus.
DispatchQueue.mainDispatchQueue.global()su atributu.serial
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Kodas vykdomas nuosekliai šioje eilėje
sharedResource += 1
print("Resource value: \(sharedResource)")
}
}
// Pavyzdys iš skirtingų srautų/eilių
// modifyResourceAsync()
// modifyResourceAsync()
- Skaitmeniniai užraktai (Reader-Writer Locks): leidžia daugeliui skaitytojų vienu metu skaityti išteklių, bet tik vienam rašytojui.
DispatchQueuesu barjerais (.barrier) rašymui ir sinchroniniu/asinchroniniu skaitymu.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Skaitymas - daug skaitytojų vienu metu
print("Reading resource: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Rašymas - tik vienas rašytojas ir niekas neskaitys/ne rašys
sharedResource = newValue
print("Writing resource: \(sharedResource)")
}
}
// Pavyzdys
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomarinės operacijos: operacijos, kurios garantuotai vykdomos visos be pertraukimo kito srauto. Naudojamos paprastiems duomenų tipams (skaitikliai, žymės).
OSAtomicIncrement,OSAtomicDecrement(pasenę, bet koncepcija aktuali)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Atomarinis padidinimas
std::cout << "Counter: " << atomicCounter << std::endl;
}
// Pavyzdys C++ kontekste iOS (naudojant Objective-C++)
// Skambinti iš skirtingų srautų
- Reikšmės tipai: Perduodant kopijas struktūrų/arba enumų, o ne nuorodas į klases, galima išvengti duomenų konkurencijos problemų, jei duomenys nėra tiesiogiai dalijamasi tarp srautų su keitimais.
Pasirinkimas priklauso nuo užduoties specifikos ir reikalaujamo paralelizmo lygio. Daugeliu atvejų iOS, rekomenduojama naudoti DispatchQueue (Serial ir Concurrent su barjerais) dėl paprastumo ir integracijos su Grand Central Dispatch (GCD).