Middle+
Kā risināt sacensību stāvokļa (race condition) problēmu?
sobes.tech AI
Atbilde no AI
Izmantojiet pavedienu sinhronizāciju kopēju resursu pieejai.
Galvenie pieejas veidi:
- Mutexi (Mūteksi): Bloķē piekļuvi resursam citiem pavedieniem, kamēr viens pavedienis to izmanto.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Noturēt mūteiks
// Kritiskā sekcija - droša pieeja sharedResource
sharedResource += 1
print("Resursa vērtība: \(sharedResource)")
lock.unlock() // Atbrīvot mūteiks
}
// Piemērs no dažādiem pavedieniem
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Sērijas rindas (Serial Queues): Izpilda uzdevumus pa vienam, garantējot, ka vienlaikus tikai viens koda bloks piekļūst resursam.
DispatchQueue.mainDispatchQueue.global()ar.serialatribītu
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Kods tiks izpildīts secīgi šajā rindā
sharedResource += 1
print("Resursa vērtība: \(sharedResource)")
}
}
// Piemērs no dažādiem pavedieniem/rindām
// modifyResourceAsync()
// modifyResourceAsync()
- Lasītāju- rakstītāju slēdzenes (Reader-Writer Locks): ļauj daudziem lasītājiem vienlaikus lasīt resursu, bet tikai vienam rakstītājam.
DispatchQueuear barjerām (.barrier) rakstīšanai un sinhronu/asociatīvu lasīšanai.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Lasīšana - daudzi lasītāji vienlaikus
print("Lasot resursu: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Rakstīšana - tikai viens rakstītājs un neviens nelasīs/ne rakstīs
sharedResource = newValue
print("Rakstot resursu: \(sharedResource)")
}
}
// Piemērs
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomārās operācijas: operācijas, kas garantēti tiek izpildītas pilnībā bez pārtraukšanas no cita pavediena. Tās ir paredzētas vienkāršiem datu tipiem (skaitītāji, karodziņi).
OSAtomicIncrement,OSAtomicDecrement(novecojuši, bet joprojām aktuāli)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Atomārais palielinājums
std::cout << "Skaitītājs: " << atomicCounter << std::endl;
}
// Piemērs C++ kontekstā iOS (ar Objective-C++)
// Izsaukt no dažādiem pavedieniem
- Vērtību tipi (Value Types): Kopiju nodošana struktūrām vai enumiem, nevis atsaucēm uz klasēm, var palīdzēt izvairīties no datu sacensību problēmām, ja dati netiek tieši dalīti starp pavedieniem ar izmaiņām.
Izvēle ir atkarīga no uzdevuma specifikas un nepieciešamā paralelizācijas līmeņa. Lielākajai daļai uzdevumu iOS ieteicams izmantot DispatchQueue (Serial un Concurrent ar barjerām) tā vienkāršības un integrācijas dēļ ar Grand Central Dispatch (GCD).