← Назад к вопросам

Какие знаешь синхронизации доступа к чтению и записи переменной?

1.8 Middle🔥 161 комментариев
#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизмы синхронизации доступа к чтению и записи переменных в iOS

В контексте многопоточного программирования под синхронизацией доступа подразумевается обеспечение корректного и безопасного обращения к общим данным из нескольких потоков одновременно. Основная проблема — гонка данных (race condition), когда результат выполнения зависит от порядка выполнения потоков. Для её решения в iOS/macOS применяется ряд механизмов.

1. Блокировки (Locks)

Мьютексы (Mutex)

Мьютекс (MUTual EXclusion) обеспечивает эксклюзивный доступ к ресурсу только одному потоку.

import Foundation

class MutexExample {
    private var mutex = pthread_mutex_t()
    private var sharedValue: Int = 0
    
    init() {
        pthread_mutex_init(&mutex, nil)
    }
    
    deinit {
        pthread_mutex_destroy(&mutex)
    }
    
    func writeValue(_ newValue: Int) {
        pthread_mutex_lock(&mutex)
        sharedValue = newValue
        pthread_mutex_unlock(&mutex)
    }
    
    func readValue() -> Int {
        pthread_mutex_lock(&mutex)
        let value = sharedValue
        pthread_mutex_unlock(&mutex)
        return value
    }
}

NSLock и pthread_mutex_t — классические реализации. Важно всегда снимать блокировку, иначе deadlock.

Рекурсивный мьютекс (Recursive Mutex)

Позволяет одному потоку захватывать одну блокировку несколько раз.

let recursiveLock = NSRecursiveLock()

Используется когда функции могут рекурсивно вызывать себя из одного потока.

Read-Write Lock

Оптимизирован для частых операций чтения: допускает множественное чтение, но эксклюзивную запись.

import Foundation

class ReadWriteLock {
    private var lock = pthread_rwlock_t()
    private var resource: String = ""
    
    init() {
        pthread_rwlock_init(&lock, nil)
    }
    
    deinit {
        pthread_rwlock_destroy(&lock)
    }
    
    var value: String {
        get {
            pthread_rwlock_rdlock(&lock)
            defer { pthread_rwlock_unlock(&lock) }
            return resource
        }
        set {
            pthread_rwlock_wrlock(&lock)
            defer { pthread_rwlock_unlock(&lock) }
            resource = newValue
        }
    }
}

2. Семафоры (Semaphores)

DispatchSemaphore позволяет ограничить количество потоков, одновременно работающих с ресурсом.

let semaphore = DispatchSemaphore(value: 1) // Двоичный семафор как мьютекс

func accessSharedResource() {
    semaphore.wait() // Уменьшает счётчик
    // Критическая секция
    semaphore.signal() // Увеличивает счётчик
}

3. Серийные очереди (Serial Queues)

DispatchQueue с атрибутом .serial обеспечивает последовательное выполнение задач.

let serialQueue = DispatchQueue(label: "com.example.serial")

var sharedData = [Int]()

func addValue(_ value: Int) {
    serialQueue.sync {
        sharedData.append(value)
    }
}

func readData() -> [Int] {
    return serialQueue.sync {
        return sharedData
    }
}

sync блокирует вызывающий поток, async — не блокирует.

4. Concurrent очереди с барьерами

Для concurrent очередей барьер (barrier) обеспечивает эксклюзивность записи при параллельном чтении.

let concurrentQueue = DispatchQueue(label: "com.example.concurrent", 
                                    attributes: .concurrent)
var dictionary = [String: Any]()

func setValue(_ value: Any, forKey key: String) {
    concurrentQueue.async(flags: .barrier) {
        dictionary[key] = value
    }
}

func getValue(forKey key: String) -> Any? {
    return concurrentQueue.sync {
        return dictionary[key]
    }
}

Барьерная задача выполняется только когда все предыдущие завершены.

5. Атомарные операции

Низкоуровневые атомарные операции через OSAtomic или Atomic свойства.

import Darwin

class AtomicCounter {
    private var counter: Int32 = 0
    
    func increment() {
        OSAtomicIncrement32(&counter)
    }
    
    var value: Int32 {
        return counter
    }
}

Важно: Многие OSAtomic функции deprecated с iOS 10. Альтернатива — std::atomic в C++ или Atomic в Swift через @atomic (доступен с Swift 5.5+ для ограниченных типов).

6. Actors (Swift 5.5+)

Actor — современная абстракция языка Swift для изоляции состояния.

actor BankAccount {
    private var balance: Double = 0.0
    
    func deposit(_ amount: Double) {
        balance += amount
    }
    
    func withdraw(_ amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
    
    var currentBalance: Double {
        return balance
    }
}

// Использование
let account = BankAccount()
Task {
    await account.deposit(100.0)
    let balance = await account.currentBalance
}

Компилятор гарантирует, что доступ к свойствам и методам actor происходит изолированно.

Критерии выбора механизма:

  • Производительность: Для частого чтения — read-write lock или concurrent queue с барьерами.
  • Безопасность: Акторы и очереди безопаснее ручных блокировок.
  • Deadlock avoidance: Избегайте вложенных блокировок, используйте .barrier или actors.
  • Уровень абстракции: Высокоуровневые решения (DispatchQueue, Actor) предпочтительнее низкоуровневых (pthread_mutex).
  • Поддержка Swift Concurrency: Для новых проектов с поддержкой iOS 13+ — Actor и @MainActor для UI.

Основная рекомендация — использовать серийные очереди для простых случаев и акторы для сложных моделей данных в современных приложениях, так как они интегрированы в систему типов Swift и исключают множество ошибок времени выполнения.

Какие знаешь синхронизации доступа к чтению и записи переменной? | PrepBro