Sobes.tech
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.
    • NSLock
    • os_unfair_lock
    • pthread_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.main
    • DispatchQueue.global() ar .serial atribī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.
    • DispatchQueue ar 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).