Какие типы очередей доступны в Grand Central Dispatch (GCD) в iOS?

Ответ

В GCD (Grand Central Dispatch) в iOS/Swift существует два основных типа очередей, классифицируемых по способу выполнения задач:

1. Последовательные очереди (Serial Queues)

Задачи выполняются строго одна за другой в порядке добавления.

  • Главная очередь (Main Queue): Глобальная последовательная очередь, связанная с основным потоком. Все операции с UI должны выполняться на ней.
    DispatchQueue.main.async {
        self.label.text = "Updated" // Безопасное обновление UI
    }
  • Пользовательские последовательные очереди (Custom Serial Queues): Создаются разработчиком.
    let serialQueue = DispatchQueue(label: "com.app.serialQueue") // По умолчанию .serial
    serialQueue.async { print("Task 1") }
    serialQueue.async { print("Task 2") } // "Task 2" начнется только после завершения "Task 1"

2. Параллельные очереди (Concurrent Queues)

Задачи могут выполняться одновременно на нескольких потоках.

  • Глобальные параллельные очереди (Global Concurrent Queues): Системные очереди с разными уровнями приоритета (Quality of Service - QoS). QoS Приоритет Назначение
    .userInteractive Высший Анимации, мгновенный отклик на действия пользователя.
    .userInitiated Высокий Задачи, инициированные пользователем, требующие быстрого результата.
    .default Средний Задачи по умолчанию.
    .utility Низкий Длительные задачи (загрузка, вычисления) с индикатором прогресса.
    .background Самый низкий Фоновые задачи, невидимые пользователю (синхронизация, чистка).
    DispatchQueue.global(qos: .utility).async {
        let data = self.loadDataFromNetwork() // Долгая задача
        DispatchQueue.main.async { self.updateUI(with: data) } // Возврат на main
    }
  • Пользовательские параллельные очереди (Custom Concurrent Queues): Создаются разработчиком.
    let concurrentQueue = DispatchQueue(label: "com.app.concurrent", attributes: .concurrent)
    concurrentQueue.async { print("Task A") }
    concurrentQueue.async { print("Task B") } // "Task A" и "Task B" могут выполняться параллельно

Ответ 18+ 🔞

О, давай разберём эту всю хуйню с очередями в GCD, а то смотрю, народ путается как слепой в борделе.

Смотри, есть всего два типа, как яйца у петуха — последовательные и параллельные. И всё, блядь.

Первые — последовательные (Serial Queues). Это как очередь в один единственный сортир на вокзале. Пока один не выйдет, следующий не зайдёт, хоть обосрись. Задачи выполняются строго по очереди, одна за другой.

Тут у нас две главных звезды:

  1. Главная очередь (Main Queue). Это святое, блядь! Это тот самый единственный поток, который может трогать твой интерфейс. Всё, что связано с кнопками, лейблами, анимациями — ВСЁ на неё. Если полезешь с этим из другого потока — получишь краш, и будет тебе как Герасиму с Муму, только в мире iOS.
    DispatchQueue.main.async {
        self.label.text = "Updated" // Вот так, блядь, правильно. Сперва делаешь всю тяжёлую работу где-то в подворотне, а потом на главную — обновить морду.
    }
  2. Свои, кастомные последовательные очереди. Создаёшь сам, называешь как хочешь. Удобно, когда нужно, чтобы задачи не лезли друг другу в жопу, а выполнялись чинно-благородно.
    let serialQueue = DispatchQueue(label: "com.app.serialQueue") // По умолчанию она уже последовательная
    serialQueue.async { print("Task 1 — я первый в сортире!") }
    serialQueue.async { print("Task 2 — я буду ждать, пока тот кончит!") } // Task 2 начнётся только после Task 1.

Вторые — параллельные (Concurrent Queues). Это уже как общественный туалет с десятью кабинками. Забегает куча народу, и каждый делает своё дело одновременно. Задачи могут выполняться в несколько потоков.

И тут тоже два лагеря:

  1. Глобальные параллельные очереди (Global Concurrent Queues). Это системные, готовые очереди. У них разный приоритет, или, как они там выёбываются, «Quality of Service» (QoS). Чтобы не запоминать, смотри табличку, я для тебя, ленивая жопа, её приготовил:

    QoS Приоритет Для чего, блядь?
    .userInteractive Высший Всё, что должно быть мгновенно: анимации, отклик на тап. Пользователь ждать не должен.
    .userInitiated Высокий Пользователь что-то нажал и ждёт быстрого результата (открыть документ).
    .default Средний Ну... по умолчанию. Если не знаешь — ставь это.
    .utility Низкий Долгие дела, где можно показать прогресс-бар: загрузка, вычисления.
    .background Самый низкий Фоновая работа, которую пользователь даже не видит: синхронизация, чистка кэша.

    Пример, чтобы не быть просто пиздаболом:

    DispatchQueue.global(qos: .utility).async {
        let data = self.loadDataFromNetwork() // Сиди тут, грузись, не мешай интерфейсу
        DispatchQueue.main.async { self.updateUI(with: data) } // А как загрузил — марш на главную, обновлять экран!
    }
  2. Свои, кастомные параллельные очереди. Создал, указал атрибут .concurrent — и вуаля, можешь запускать задачи пачками.
    let concurrentQueue = DispatchQueue(label: "com.app.concurrent", attributes: .concurrent)
    concurrentQueue.async { print("Task A — я пошёл!") }
    concurrentQueue.async { print("Task B — и я тоже пошёл! Мы можем работать вместе!") } // Оба могут выполняться одновременно.

Вот и вся магия, ёпта. Главное — запомни: с UI работаешь только на главной очереди, а всю тягомотину скидывай в глобальные с подходящим приоритетом. А то накосячишь — и будет тебе не мультитрединг, а один сплошной пиздец.