В каких случаях применяется барьерная операция (dispatch barrier) в GCD?

Ответ

Барьерная операция (dispatch_barrier_async) используется в конкурентной очереди для создания точки синхронизации, обеспечивая атомарность записи при параллельных чтениях (шаблон "чтение-запись").

Принцип работы: Все задачи, добавленные в очередь до барьера, выполняются параллельно. Барьерная задача выполняется только одна и монопольно — очередь приостанавливает запуск новых задач до её завершения. Задачи после барьера снова выполняются параллельно.

Пример использования для thread-safe контейнера:

class ThreadSafeContainer<T> {
    private var storage = [T]()
    private let queue = DispatchQueue(label: "com.example.container", attributes: .concurrent)

    func read() -> [T] {
        return queue.sync { // Множество параллельных чтений
            return storage
        }
    }

    func write(_ element: T) {
        queue.async(flags: .barrier) { // Одна эксклюзивная запись
            self.storage.append(element)
        }
    }
}

Типичные сценарии:

  • Кэширование данных (NSCache).
  • Работа с внутренними структурами данных, требующими безопасного доступа из многих потоков.
  • Инициализация или обновление общих ресурсов (конфигурация, файлы).

Важно: Барьеры работают только с очередями, созданными с атрибутом .concurrent. На серийных очередях они не имеют особого эффекта.

Ответ 18+ 🔞

А, ну вот, смотри, опять про эти ваши барьеры, блядь. Ну, типа, представь себе толпу мудаков, которые все одновременно лезут в один холодильник за пивом. Чтение — это когда они смотрят, что там есть. И пусть смотрят все сразу, нахуй, им же не жалко. Параллельно, блядь.

А вот когда кому-то приспичило положить в этот холодильник новую банку — вот тут-то и начинается пиздец. Если не синхронизировать, то один будет засовывать, другой в это же время вынимать, и в итоге все закончится разбитым стеклом и пивом на полу. Э, сабака сука!

Так вот, барьерная операция — это как здоровенный, блядь, Герасим из той истории, который встаёт в дверном проёме и орет: "ВСЁ, СУКА, СТОП!". Все, кто уже полезли читать — читают. Но новые читатели ждут. А тот, кто пришёл писать (это и есть барьерная задача), заходит один, делает своё чёрное дело, кладёт свою банку, и только потом, когда он ушёл, все остальные снова начинают свою возню.

Вот, смотри на этот код, там всё как раз про это:

class ThreadSafeContainer<T> {
    private var storage = [T]()
    private let queue = DispatchQueue(label: "com.example.container", attributes: .concurrent) // <-- ВАЖНО! Очередь должна быть КОНКУРЕНТНОЙ, а то нихуя не сработает!

    func read() -> [T] {
        return queue.sync { // Множество параллельных чтений
            return storage
        }
    }

    func write(_ element: T) {
        queue.async(flags: .barrier) { // Одна эксклюзивная запись
            self.storage.append(element)
        }
    }
}

Видишь? read — обычный sync, лезьте все, блядь, смотрите. А write — с флагом .barrier. Это и есть наш Герасим. Он один, ёпта, и он всех блокирует, пока не закончит.

Где это надо? Да везде, где общая шняга! Кэш, какая-нибудь конфигурация, которую все потоки юзают, да та же самая, блядь, база данных в памяти. Всё, где много читают, но редко пишут.

И главное, запомни, чувак: барьер — это не магия, а просто особый флаг для конкурентной очереди. Если ты его на серийную очередь навесишь — так он нихуя не сделает, она и так одна задача за раз выполняет. Так что не выёбывайся, создавай очередь с .concurrent, а потом уже барьерьььь... В рот меня чих-пых!