Middle+
Ինչպե՞ս լուծել մրցակցության խնդիր (race condition):
sobes.tech AI
Պատասխան AI-ից
Կծկումային հոսքերի սինխրոնիզացիայի օգտագործում ընդհանուր ռեսուրսներին մուտք գործելու համար:
Հիմնական մոտեցումները՝
- Mutexes (Մյուտեքսներ): Բլոկավորում են ռեսուրսի մուտքը մյուս հոսքերին, մինչեւ մեկը օգտագործում է այն:
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Մյուտեքսի գրավում
// Կրիտիկական հատված - անվտանգ մուտք դեպի sharedResource
sharedResource += 1
print("Ռեսուրսի արժեքը: \(sharedResource)")
lock.unlock() // Մյուտեքսի ազատում
}
// Օրինակ՝ տարբեր հոսքերից կանչում
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Սերիական հերթեր (Serial Queues): Կատարում են առաջադրանքները մեկը մյուսի հետևից, ապահովելով, որ միայն մեկ կոդի բլոկ ունի մուտք ռեսուրս:
DispatchQueue.mainDispatchQueue.global()՝.serialհատկությամբ
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Կոդը կատարվում է հերթականությամբ
sharedResource += 1
print("Ռեսուրսի արժեքը: \(sharedResource)")
}
}
// Օրինակ՝ տարբեր հոսքերից/հերթերից կանչում
// modifyResourceAsync()
// modifyResourceAsync()
- Կարդացող-գրող (Reader-Writer) Lock-եր: Թույլատրում են միաժամանակ մի քանի հոսքերին կարդալ ռեսուրսը, բայց միայն մեկին գրել:
DispatchQueue՝ բարիեր (.barrier) գրառման համար և սինխրոն/ասինխրոն մուտքագրում՝ կարդալու համար:
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Կարդալ - միաժամանակ շատ ընթերցողներ
print("Կարդում ռեսուրսը: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Գրառում - միայն մեկ գրող և ոչ ոք չի կարդում/գրում
sharedResource = newValue
print("Գրառում ռեսուրսում: \(sharedResource)")
}
}
// Օրինակ՝ կանչեր
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Ատոմարային (Atomic) օպերացիաներ: Գործողություններ, որոնք կատարվում են ամբողջությամբ առանց ընդհատումների այլ հոսքից:
OSAtomicIncrement,OSAtomicDecrement(հին, բայց հասկացությունը актуալ է)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Ատոմարային ավելացում
std::cout << "Համարիչ: " << atomicCounter << std::endl;
}
// Օրինակ՝ C++-ում iOS կոնտեքստում (օգտագործելով Objective-C++)
// Կոչել տարբեր հոսքերում
- Արժեքային տիպեր (Value Types): Շինվածքների կամ ցուցակների պատճենների փոխանցումը կարող է օգնել խուսափել race condition-ներից, եթե տվյալները չեն բաժանվում ուղղակիորեն հոսքերի միջև փոփոխվող տեսքով:
Ընտրությունը կախված է առաջադրանքի բնույթից և անհրաժեշտ պարալելիզմի մակարդակից:
Ընդհանուր առմամբ, iOS-ում DispatchQueue (սերիական և համաժամանակյա բարիերներով) օգտագործելը առավել հարմար է, քանի որ հեշտ է օգտագործել և լավ ինտեգրված է Grand Central Dispatch (GCD)-ի հետ: