Middle+
Kuidas lahendada võistlusolukorra (race condition) probleem?
sobes.tech AI
Vastus AI-lt
Kasuta niidide sünkroneerimist ühiste ressursside juurdepääsu jaoks.
Peamised lähenemisviisid:
- Mutexid (Muteksid): Blokeerivad juurdepääsu ressursile teistele niitidele, kuni üks niit kasutab seda.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Muteksi lukustamine
// Kriitiline sektsioon - turvaline juurdepääs sharedResource'ile
sharedResource += 1
print("Resursside väärtus: \(sharedResource)")
lock.unlock() // Muteksi vabastamine
}
// Näide erinevatest niitidest
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Süntesaatorid (Serial Queues): Täidavad ülesandeid ükshaaval, tagades, et korraga on ainult üks koodiplokk juurdepääs ressursile.
DispatchQueue.mainDispatchQueue.global()koos.serialatribuudiga
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Kood täidetakse järjekorras
sharedResource += 1
print("Resursside väärtus: \(sharedResource)")
}
}
// Näide erinevatest niitidest/järjestustest
// modifyResourceAsync()
// modifyResourceAsync()
- Lugejate-Kirjutajate lukud (Reader-Writer Locks): võimaldavad paljudel lugijatel samaaegselt lugeda ressurssi, kuid ainult ühe kirjutaja.
DispatchQueuebarjääridega (.barrier) kirjutamiseks ja sünkroneerimata/asinkroneerimata lugemiseks.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Lugemine - paljud lugejad samaaegselt
print("Lugedes ressurssi: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Kirjutamine - ainult üks kirjutaja ja keegi ei loe ega kirjuta
sharedResource = newValue
print("Kirjutades ressurssi: \(sharedResource)")
}
}
// Näide
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomaarsed operatsioonid: operatsioonid, mis täidetakse garanteeritult kogu aeg ilma teiste niitide katkestusteta. Kasutatakse lihtsate andmetüüpide (loendurid, lipud) puhul.
OSAtomicIncrement,OSAtomicDecrement(vananenud, kuid kontseptsioon kehtib)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Atomaarselt suurendamine
std::cout << "Loendur: " << atomicCounter << std::endl;
}
// Näide C++-s iOS kontekstis (kasutades Objective-C++)
// Kutsub erinevatest niitidest
- Väärtusetüübid (Value Types): Struktuuride või enumide koopiate edastamine, mitte viidete kasutamine klassidele, võib aidata vältida andmekonkurentsi probleeme, kui andmed ei jagata otse muutmise eesmärgil niitide vahel.
Valik sõltub ülesande spetsiifikast ja nõutavast paralleelismitasemest. Enamasti iOS-is on soovitatav kasutada DispatchQueue (Serial ja Concurrent barjääridega) lihtsuse ja GCD-ga integreerimise tõttu.