Sobes.tech
Middle

Jakie są sposoby zapewnienia bezpieczeństwa wątków w aplikacjach iOS?

sobes.tech AI

Odpowiedź od AI

Aby zapewnić bezpieczeństwo wątków w iOS, używane są następujące główne mechanizmy:

  1. Mutex (mutual exclusion):

    • pthread_mutex_t: niskopoziomowe API w C z POSIX.
    • NSLock: opakowanie obiektowe nad prymitywami blokowania, część Foundation.
    • NSRecursiveLock: pozwala na rekurencyjne blokowanie tym samym wątkiem.
    import Foundation
    
    let lock = NSLock()
    var zasóbWspółdzielony = 0
    
    func modyfikujZasóbWspółdzielony() {
        lock.lock()
        // Sekcja krytyczna
        zasóbWspółdzielony += 1
        lock.unlock()
    }
    
  2. Spin Lock:

    • OSSpinLock: przestarzały i niezalecany z powodu problemów z priorytetami i nadmiernym zużyciem CPU podczas długiego oczekiwania. Zastąpiony przez os_unfair_lock.
    • os_unfair_lock: bardziej wydajna zamiana OSSpinLock, część os.xnu.
    import os.lock
    
    var unfairLock = os_unfair_lock()
    var innyZasób = 0
    
    func aktualizujZasób() {
        os_unfair_lock_lock(&unfairLock)
        // Sekcja krytyczna
        innyZasób += 1
        os_unfair_lock_unlock(&unfairLock)
    }
    
  3. Semafor:

    • DispatchSemaphore: semafor z Grand Central Dispatch (GCD). Zarządza dostępem do zasobu za pomocą licznika.
    import Foundation
    
    let semaphore = DispatchSemaphore(value: 1) // Licznik = 1 (odpowiednik mutexa)
    var ograniczonyZasób = 0
    
    func uzyskajOgraniczonyZasób() {
        semaphore.wait() // Zmniejsza licznik, blokuje jeśli <= 0
        // Sekcja krytyczna
        ograniczonyZasób += 1
        semaphore.signal() // Zwiększa licznik
    }
    
  4. Kolejka współbieżna z zadaniami barierowymi:

    • Użycie kolejki współbieżnej w GCD do odczytu i zapisu. Odczyt wykonywany równolegle (async), zapis wyłącznie (sync(flags: .barrier)).
    import Foundation
    
    let kolejkaWspółbieżna = DispatchQueue(label: "com.przyklad.kolejkaWspółbieżna", attributes: .concurrent)
    var chronionaTablica: [Int] = []
    
    func dodajElement(_ element: Int) {
        kolejkaWspółbieżna.sync(flags: .barrier) {
            // Zadanie z flagą .barrier wykonywane wyłącznie
            chronionaTablica.append(element)
        }
    }
    
    func odczytZTablicy() -> [Int] {
        var kopiaTablicy: [Int] = []
        kolejkaWspółbieżna.sync {
            // Normalne zadanie synchroniczne (odczyt), może być równoległe do innych odczytów
            kopiaTablicy = chronionaTablica
        }
        return kopiaTablicy
    }
    
  5. Operacje atomowe:

    • Niskopoziomowe operacje gwarantujące atomowość (wykonywane jako jedna całość, bez przerwań). Używane dla prostych typów danych (całkowite, wskaźniki).
    import Darwin // Dla niektórych funkcji atomowych, np. OSAtomic...
    
    var atomowyLicznik: Int32 = 0
    
    func inkrementujAtomowyLicznik() -> Int32 {
        // Przykład operacji atomowej
        return OSAtomicIncrement32(&atomowyLicznik)
    }
    
    • W Swift dostępne są eksperymentalne typy Atomic<Value> w swift-atomics.
  6. Sanitizer wątków:

    • Narzędzie debugowania, które wykrywa wyścigi danych (data races) podczas wykonywania aplikacji. Włącza się je w schemacie kompilacji Xcode (Edit Scheme -> Run -> Diagnostics -> Thread Sanitizer).

Wybór mechanizmu zależy od zadania:

  • Prosty dostęp wyłączny: NSLock, DispatchSemaphore(value: 1), os_unfair_lock.
  • Rekursywne blokady: NSRecursiveLock.
  • Odczyt/zapis: kolejka współbieżna z .barrier.
  • Ograniczenie dostępu (pula zasobów): DispatchSemaphore (z licznikiem > 1).
  • Proste liczniki/flagi: operacje atomowe (jeśli mają zastosowanie i wymagana jest maksymalna wydajność, ale ostrożnie).

Unikać należy:

  • OSSpinLock.
  • Używania globalnych blokad bez potrzeby.
  • Wczesnej optymalizacji za pomocą niskopoziomowych prymitywów bez zrozumienia ich działania.