Какие основные методы у DispatchGroup в Swift и для чего они используются?

Ответ

DispatchGroup — инструмент Grand Central Dispatch (GCD) для синхронизации выполнения группы асинхронных задач. Позволяет дождаться завершения всех задач перед выполнением финального действия.

Ключевые методы:

Метод Назначение
enter() Увеличивает внутренний счетчик группы на 1, отмечая начало новой асинхронной задачи.
leave() Уменьшает счетчик на 1, сигнализируя о завершении задачи. Должен вызываться ровно столько раз, сколько enter().
notify(queue:execute:) Задает замыкание, которое выполнится на указанной очереди, когда счетчик группы станет равным 0. Не блокирует текущий поток.
wait() Блокирует текущий поток до тех пор, пока счетчик группы не станет 0. Может принимать timeout.

Базовый шаблон использования:

let group = DispatchGroup()
let queue = DispatchQueue.global(qos: .userInitiated)

// Задача 1
group.enter()
queue.async {
    defer { group.leave() } // Гарантированный вызов leave()
    // Выполнение асинхронной работы
    downloadImage(from: url1)
}

// Задача 2
group.enter()
queue.async {
    defer { group.leave() }
    // Другая асинхронная работа
    processData()
}

// Действие после завершения ВСЕХ задач
group.notify(queue: .main) {
    // Обновление UI на главном потоке
    self.updateInterface()
    print("All tasks completed")
}

Важные нюансы:

  1. Баланс enter/leave: Несбалансированные вызовы приведут к вечному ожиданию (deadlock) или преждевременному срабатыванию notify.
  2. Использование defer: Рекомендуется вызывать leave() внутри defer в начале блока задачи, чтобы гарантировать его выполнение даже при возникновении ошибки.
  3. wait() vs notify(): wait() блокирует поток и обычно используется в фоновых очередях. notify() — неблокирующий, предпочтителен для большинства сценариев.
  4. Таймаут для wait():
    if group.wait(timeout: .now() + 5.0) == .timedOut {
        print("Tasks took too long")
    }

Ответ 18+ 🔞

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

Ключевые методы, на которые надо смотреть, как на икону:

Метод Назначение (для особо одарённых)
enter() Ты как бы говоришь: "Эй, группа, я сейчас ещё одну хуйню запущу, считай её". Счётчик внутри плюсуется.
leave() А это когда ты, наконец-то, эту хуйню закончил. Счётчик минусуется. Вызовишь leave() меньше раз, чем enter() — будешь ждать до второго пришествия, мудак.
notify(queue:execute:) Это твоя финальная плюха. Задаёшь замыкание, которое выстрелит, когда все твои дела будут в ажуре (счётчик = 0). Текущий поток не ебёт, он свободен.
wait() А вот это уже по-пацански. Блокирует текущий поток нахуй, пока все не сделают. Сиди и не дыши. Можно дать таймаут, чтобы не заснуть навечно.

Смотри, как это обычно выглядит, чтобы не облажаться:

let group = DispatchGroup() // Создал свою банду
let queue = DispatchQueue.global(qos: .userInitiated) // Фоновая очередь, где вся работа идёт

// Запускаем первую подзадачу, например, картинку качать
group.enter() // Кричим: "Группа, внимание, я начинаю!"
queue.async {
    defer { group.leave() } // Святое правило: пишешь это СРАЗУ. Даже если всё пиздец, `leave()` вызовется.
    // Тут твоя ебля с загрузкой
    downloadImage(from: url1)
}

// Вторая подзадача, например, данные обработать
group.enter() // Опять: "Группа, я ещё одну!"
queue.async {
    defer { group.leave() } // Опять гарантия, ёпта!
    // Тут другая ебля
    processData()
}

// И вот когда ВСЁ, блядь, закончится, делаем финальный аккорд
group.notify(queue: .main) { // На главной очереди, чтобы UI не сломал
    self.updateInterface() // Обновляем интерфейс
    print("All tasks completed, ура, блядь!")
}

А теперь, слушай сюда, главные подводные ебли, о которые все расшибаются:

  1. Баланс enter/leave — это святое. Вызвал enter() — будь добр, вызови leave(). Не сбалансируешь — либо notify никогда не выстрелит (deadlock, пиши пропало), либо выстрелит раньше, чем надо. Пиздец и позор.
  2. defer — твой лучший друг. Засунул defer { group.leave() } в самое начало блока задачи — и спи спокойно. Выполнится при любом раскладе: и если всё ок, и если вылетела ошибка. Волнение ебать — ноль.
  3. wait() или notify()? wait() — это когда ты готов заблокировать текущий поток и ждать, как лох у парадного. Используй в фоновых очередях. notify() — цивилизованно, неблокирующе, для большинства случаев.
  4. Таймаут для wait() — признак ума. А то вдруг твои задачи зависли навечно?
    if group.wait(timeout: .now() + 5.0) == .timedOut {
        print("Задачи заебали, я ушёл. Таймаут, сука!")
    }

Вот и вся магия. Главное — не перепутай enter с leave, а то будет тебе, как Герасиму с Муму, только грустнее.