Что такое Dispatch Barrier в GCD?

Ответ

Dispatch Barrier — это механизм в Grand Central Dispatch (GCD), обеспечивающий эксклюзивный доступ к ресурсу при работе с concurrent (параллельной) очередью. Он временно превращает параллельную очередь в последовательную для выполнения конкретной задачи.

Принцип работы:

  1. До барьера: Задачи в concurrent очереди выполняются параллельно.
  2. Барьерная задача: Задача, отправленная с флагом .barrier, ждет завершения всех уже запущенных задач.
  3. Во время барьера: Очередь становится последовательной, и барьерная задача выполняется одна.
  4. После барьера: Очередь снова становится concurrent для следующих задач.

Типичное применение: Создание потокобезопасных структур данных с оптимизацией для частого чтения (reader-writer lock).

Пример (Потокобезопасный кэш в Swift):

class ThreadSafeCache<Key: Hashable, Value> {
    private let concurrentQueue = DispatchQueue(label: "com.cache.queue", attributes: .concurrent)
    private var storage: [Key: Value] = [:]

    // Чтение (параллельное, не блокирующее)
    func getValue(for key: Key) -> Value? {
        return concurrentQueue.sync {
            storage[key]
        }
    }

    // Запись (эксклюзивная через барьер)
    func setValue(_ value: Value, for key: Key) {
        concurrentQueue.async(flags: .barrier) {
            self.storage[key] = value
        }
    }
}

Важное ограничение: Барьеры работают только с пользовательскими concurrent очередями, созданными с атрибутом .concurrent. Использование с глобальными очередями (DispatchQueue.global()) или главной очередью (DispatchQueue.main) не имеет эффекта.

Ответ 18+ 🔞

Слушай, а вот есть такая штука — Dispatch Barrier. Ну, в GCD, понимаешь? Это как такой умный засранец, который умеет временно превращать параллельную толкучку в упорядоченную очередь из одного человека, чтобы тот мог спокойно сделать своё чёрное дело без помех.

Как эта магия работает, если на пальцах:

  1. До барьера: Все задачи в concurrent очереди носятся как угорелые, кто во что горазд, параллельно.
  2. Приходит Барьер: Задача, которую отправили с флагом .barrier, такая: "Стоять, блядь! Все закончили то, что уже начали? А теперь все нахуй пошли, я буду работать один". И очередь замирает.
  3. Во время барьера: Очередь становится последовательной, и эта барьерная сволочь делает что хочет, пока все остальные ждут.
  4. После барьера: Как только барьерная задача закончила — "Разбежались!" — и снова начинается привычный параллельный адок.

Где это, блядь, применить? Ну классика — сделать потокобезопасную хуйню, которую часто читают, но редко пишут. Типа кэша. Читать могут все сразу, а писать — только один, и чтобы в этот момент его никто не трогал.

Смотри, как это выглядит в коде (потокобезопасный кэш на Swift):

class ThreadSafeCache<Key: Hashable, Value> {
    // Создаём свою concurrent очередь, а не берём первую попавшуюся глобальную
    private let concurrentQueue = DispatchQueue(label: "com.cache.queue", attributes: .concurrent)
    private var storage: [Key: Value] = [:]

    // Чтение — быстрое, параллельное, без барьеров. Все могут лезть одновременно.
    func getValue(for key: Key) -> Value? {
        return concurrentQueue.sync {
            storage[key]
        }
    }

    // А вот запись — тут уже барьер, сука. Всех остановили, записали, потом отпустили.
    func setValue(_ value: Value, for key: Key) {
        concurrentQueue.async(flags: .barrier) {
            self.storage[key] = value
        }
    }
}

И главное, ёпта, предупреждение, а то накосячите: Барьеры работают ТОЛЬКО с очередями, которые ты сам создал с атрибутом .concurrent. Если ты попробуешь впендюрить барьер в какую-нибудь системную глобальную очередь (DispatchQueue.global()) или, не дай бог, в главную (DispatchQueue.main) — нихуя не произойдёт, он проигнорируется. Так что не выёбывайся, создавай свою очередь и там уже хуячь барьеры.