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

Как сделать атомарную функцию на GCD?

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

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

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

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

Реализация атомарных операций с использованием GCD

Атомарность в GCD достигается через создание изолированных контекстов выполнения, где доступ к разделяемым ресурсам синхронизируется. Основные подходы:

1. Serial Dispatch Queue (Последовательная очередь)

Самый распространённый метод — использование serial очереди для эксклюзивного доступа к ресурсу:

class AtomicValue<T> {
    private var _value: T
    private let queue = DispatchQueue(label: "com.example.atomic.queue")
    
    init(_ value: T) {
        self._value = value
    }
    
    var value: T {
        get {
            return queue.sync { _value }
        }
        set {
            queue.sync { _value = newValue }
        }
    }
    
    func mutate(_ transform: (inout T) -> Void) {
        queue.sync {
            transform(&_value)
        }
    }
}

// Использование
let atomicCounter = AtomicValue(0)
atomicCounter.mutate { $0 += 1 } // Атомарная операция

2. Concurrent Queue + Barrier (Барьерные операции)

Для более эффективного чтения с возможностью эксклюзивной записи:

class ConcurrentAtomic<T> {
    private var _value: T
    private let queue = DispatchQueue(
        label: "com.example.concurrent.atomic",
        attributes: .concurrent
    )
    
    init(_ value: T) {
        self._value = value
    }
    
    var value: T {
        get {
            return queue.sync { _value } // Обычное чтение
        }
    }
    
    func setValue(_ newValue: T) {
        queue.async(flags: .barrier) {
            self._value = newValue // Эксклюзивная запись
        }
    }
    
    func mutate(_ transform: @escaping (inout T) -> Void) {
        queue.async(flags: .barrier) {
            transform(&self._value)
        }
    }
}

Барьерные операции гарантируют, что при выполнении .barrier задачи, все остальные задачи в очереди будут ждать её завершения.

3. DispatchSemaphore (Семафоры)

Для низкоуровневой синхронизации:

class AtomicWithSemaphore {
    private var value: Int = 0
    private let semaphore = DispatchSemaphore(value: 1)
    
    func increment() {
        semaphore.wait() // Захват ресурса
        defer { semaphore.signal() } // Освобождение
        
        value += 1 // Критическая секция
    }
    
    func getValue() -> Int {
        semaphore.wait()
        defer { semaphore.signal() }
        
        return value
    }
}

4. Actor Model (Swift 5.5+)

Современный подход с использованием акторов:

actor AtomicCounter {
    private var count = 0
    
    func increment() {
        count += 1
    }
    
    func getValue() -> Int {
        return count
    }
}

// Использование
Task {
    let counter = AtomicCounter()
    await counter.increment() // Автоматическая атомарность
}

Ключевые принципы атомарности в GCD

  • Изоляция состояния: Все модификации разделяемого ресурса должны происходить через один и тот же механизм синхронизации
  • Отсутствие гонок данных: Гарантия, что между чтением и записью значения не произойдёт конкурирующей операции
  • Потокобезопасность: Корректная работа из любого количества потоков
  • Детерминированность: Результат операций не зависит от порядка планирования в многопоточной среде

Практические рекомендации

  1. Для простых случаев используйте serial очереди — они понятны и надежны
  2. Для read-heavy workloads применяйте concurrent очереди с barrier для записи
  3. Избегайте priority inversion — используйте .default приоритет для синхронизационных очередей
  4. Минимизируйте время удержания блокировки — выполняйте в критической секции только необходимые операции
  5. Используйте sync с осторожностью — чтобы избежать deadlock при реентерабельных вызовах

Пример сложной атомарной операции

class AtomicDictionary<Key: Hashable, Value> {
    private var dictionary: [Key: Value] = [:]
    private let queue = DispatchQueue(
        label: "com.example.atomic.dict",
        attributes: .concurrent
    )
    
    func get(_ key: Key) -> Value? {
        return queue.sync {
            dictionary[key]
        }
    }
    
    func set(_ value: Value, forKey key: Key) {
        queue.async(flags: .barrier) {
            self.dictionary[key] = value
        }
    }
    
    func remove(_ key: Key) {
        queue.async(flags: .barrier) {
            self.dictionary.removeValue(forKey: key)
        }
    }
    
    func mutate(_ key: Key, transform: (inout Value?) -> Void) {
        queue.async(flags: .barrier) {
            transform(&self.dictionary[key])
        }
    }
}

Главное преимущество GCD для атомарных операций — абстракция от низкоуровневых примитивов и интеграция с существующей асинхронной инфраструктурой приложений. При этом важно помнить о правильном выборе QoS и избегании взаимных блокировок при проектировании атомарных примитивов.

Как сделать атомарную функцию на GCD? | PrepBro