Sobes.tech
Middle+

Kako rešiti problem trka (race condition)?

sobes.tech АИ

Одговор од АИ

Korišćenje sinhronizacije niti za pristup zajedničkim resursima.

Glavni pristupi:

  • Muteksi (Zaključavanje): Blokiraju pristup resursu drugim nitima dok jedna nit koristi resurs.
    • NSLock
    • os_unfair_lock
    • pthread_mutex_t
import Foundation

let lock = NSLock()
var sharedResource = 0

func modifyResource() {
    lock.lock() // Zauzimanje muteksa
    // Kritična sekcija - siguran pristup sharedResource
    sharedResource += 1
    print("Vrednost resursa: \(sharedResource)")
    lock.unlock() // Oslobađanje muteksa
}

// Primer poziva iz različitih niti
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
  • Serijska reda (Serial Queues): Izvršavaju zadatke jedan za drugim, garantujući da samo jedan blok koda istovremeno ima pristup resursu.
    • DispatchQueue.main
    • DispatchQueue.global() sa atributom .serial
import Foundation

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

func modifyResourceAsync() {
    serialQueue.async {
        // Kod se izvršava sekvencijalno
        sharedResource += 1
        print("Vrednost resursa: \(sharedResource)")
    }
}

// Primer poziva iz različitih niti/redova
// modifyResourceAsync()
// modifyResourceAsync()
  • Čitaoci-Pisci (Reader-Writer) Lockovi: Omogućavaju više čitaoca da čitaju istovremeno, ali samo jedan piše.
    • DispatchQueue sa barijerama (.barrier) za zapis i sinhronim/asinkronim pristupom za čitanje.
import Foundation

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

func readResource() {
    concurrentQueue.async { // Čitanje - više čitalaca istovremeno
        print("Čitanje resursa: \(sharedResource)")
    }
}

func writeResource(newValue: Int) {
    concurrentQueue.sync(flags: .barrier) { // Zapis - samo jedan pisac i niko ne čita/ne piše
        sharedResource = newValue
        print("Pisanje u resurs: \(sharedResource)")
    }
}

// Primer poziva
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
  • Atomične operacije: Operacije koje se garantovano izvršavaju celovito bez prekida od strane drugog toka. Koriste se za jednostavne tipove podataka (brojači, zastavice).
    • OSAtomicIncrement, OSAtomicDecrement (zastarelo, ali koncept je i dalje relevantan)
    • C++11 <atomic>
#include <atomic>
#include <iostream>

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

void incrementCounter() {
    atomicCounter++; // Atomsko inkrementiranje
    std::cout << "Brojač: " << atomicCounter << std::endl;
}

// Primer C++ u kontekstu iOS (korišćenje sa Objective-C++)
// Pozvati iz različitih niti
  • Tipovi vrednosti: Prenos kopija struktura ili enumeracija može pomoći u izbegavanju problema sa race condition ako podaci nisu direktno deljeni između niti u promenljivom obliku.

Izbor metode zavisi od specifičnosti zadatka i nivoa paralelizma koji je potreban. Za većinu zadataka u iOS, preporučuju se DispatchQueue (serijski i paralelni sa barijerama) zbog jednostavnosti korišćenja i integracije sa Grand Central Dispatch (GCD).