Sobes.tech
Middle+

როგორ გადავჭრათ race condition-ის პრობლემა?

sobes.tech AI

პასუხი AI-სგან

პირადი რესურსების წვდომის ნაკადი სინქრონიზაციის გამოყენება.

ძირითადი მიდგომები:

  • Mutexes (მიუტექსები): ბლოკავს რესურსის წვდომას სხვა ნაკადებისთვის, სანამ ერთი ნაკადი იყენებს მას.
    • NSLock
    • os_unfair_lock
    • pthread_mutex_t
import Foundation

let lock = NSLock()
var sharedResource = 0

func modifyResource() {
    lock.lock() // მიუტექსის დაჭერა
    // კრიტიკული სექცია - უსაფრთხო წვდომა sharedResource-ზე
    sharedResource += 1
    print("Resource value: \(sharedResource)")
    lock.unlock() // მიუტექსის გათავისუფლება
}

// მაგალითი სხვადასხვა ნაკადიდან გამოძახებისთვის
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
  • Serial Queues (სერიული რიგები): ასრულებს დავალებებს ერთზე შემდეგ, გარანტიას იძლევა, რომ ერთდროულად მხოლოდ ერთი ბლოკი აქვს წვდომა რესურსზე.
    • DispatchQueue.main
    • DispatchQueue.global() ატრიბუტით .serial
import Foundation

let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0

func modifyResourceAsync() {
    serialQueue.async {
        // კოდი შესრულდება სერიულად ამ რიგში
        sharedResource += 1
        print("Resource value: \(sharedResource)")
    }
}

// მაგალითი სხვადასხვა ნაკადიდან/რიგიდან გამოძახებისთვის
// modifyResourceAsync()
// modifyResourceAsync()
  • Reader-Writer Locks (კითხვის-წერის ბლოკირება): საშუალებას აძლევს მრავლისმნახველ ნაკადებს ერთდროულად წაიკითხონ რესურსი, მაგრამ მხოლოდ ერთმა წერის ნაკადმა.
    • DispatchQueue ბარიერებით (.barrier) წერისთვის და სინქრონული/ასინქრონული წაკითხვისთვის.
import Foundation

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0

func readResource() {
    concurrentQueue.async { // კითხვა - მრავალი მკითხველი ერთდროულად
        print("Reading resource: \(sharedResource)")
    }
}

func writeResource(newValue: Int) {
    concurrentQueue.sync(flags: .barrier) { // წერა - მხოლოდ ერთი მწერალი და არავინ კითხულობს/წერს
        sharedResource = newValue
        print("Writing resource: \(sharedResource)")
    }
}

// მაგალითები
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
  • Atomic Operations (ატომური ოპერაციები): ოპერაციები, რომლებიც გარანტირებულად სრულდება მთლიანად შეწყვეტის გარეშე სხვა ნაკადის მიერ. გამოიყენება მარტივი ტიპების (სათვალთვალო, ფლაგები)თვის.
    • OSAtomicIncrement, OSAtomicDecrement (ძველი, მაგრამ კონცეფცია აქტუალურია)
    • C++11 <atomic>
#include <atomic>
#include <iostream>

std::atomic<int> atomicCounter(0);

void incrementCounter() {
    atomicCounter++; // ატომური ინკრემენტი
    std::cout << "Counter: " << atomicCounter << std::endl;
}

// მაგალითი C++-ში iOS კონტექსტში (გამოყენება Objective-C++-თან)
// გამოძახება სხვადასხვა ნაკადიდან
  • Value Types (მნიშვნელობის ტიპები): სტრუქტურების/სიის კოპირების გადაცემა ნაცვლად კლასების რგოლების, შეიძლება დაეხმაროს მონაცემთა შეჯახების პრობლემების თავიდან აცილებაში, თუ მონაცემები პირდაპირ არ იყოფა სხვადასხვა ნაკადში ცვლილებებისთვის.

მეთოდის არჩევანი დამოკიდებულია ამოცანის სპეციფიკაზე და პარალელიზმის საჭირო დონეზე. უმეტეს შემთხვევაში iOS-ში სასურველია DispatchQueue (სერიული და კონკურენტული ბარიერებით) გამოყენება, რადგან ის მარტივია და ინტეგრირებულია GCD-სთან.