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

Какие знаешь виды очередей?

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

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

Виды очередей в Swift и iOS

Типы очередей (Dispatch Queues)

1. Main Queue — главная очередь

// Основная очередь для UI операций
DispatchQueue.main.async {
    // Все UI обновления здесь
    self.label.text = "Updated"
    self.imageView.image = newImage
}

// Синхронно (опасно!)
DispatchQueue.main.sync {  // Может привести к deadlock
    self.label.text = "Updated"
}

Характеристики:

  • Serial queue (одна операция за раз)
  • Запускается на главном потоке
  • Для всех UI операций
  • Обновления экрана синхронизированы с refresh rate (60/120 FPS)

2. Global Queues — глобальные очереди

// QoS (Quality of Service) levels
DispatchQueue.global(qos: .userInteractive).async {
    // Максимальный приоритет, для UI-related work
}

DispatchQueue.global(qos: .userInitiated).async {
    // Высокий приоритет, пользователь ждет результат
    let data = fetchDataFromAPI()
    DispatchQueue.main.async {
        self.updateUI(with: data)
    }
}

DispatchQueue.global(qos: .default).async {
    // Стандартный приоритет
}

DispatchQueue.global(qos: .utility).async {
    // Низкий приоритет, долгие операции
    let result = expensiveComputation()
}

DispatchQueue.global(qos: .background).async {
    // Минимальный приоритет, фоновые задачи
    let backup = createBackup()
}

QoS Levels:

┌─────────────────────────────────────────────┐
│ .userInteractive   │ Главное UI, ~25ms     │
├─────────────────────────────────────────────┤
│ .userInitiated     │ Действия пользователя │
├─────────────────────────────────────────────┤
│ .default           │ Стандартный приоритет │
├─────────────────────────────────────────────┤
│ .utility           │ Долгие операции       │
├─────────────────────────────────────────────┤
│ .background        │ Фоновые задачи        │
└─────────────────────────────────────────────┘

3. Custom Queues — пользовательские очереди

// Serial queue (одна операция за раз)
let serialQueue = DispatchQueue(label: "com.example.serial")

serialQueue.async {
    print("1")
}
serialQueue.async {
    print("2")
}
serialQueue.async {
    print("3")
}
// Вывод: 1, 2, 3 (в таком порядке, не параллельно)

// Concurrent queue (несколько операций одновременно)
let concurrentQueue = DispatchQueue(
    label: "com.example.concurrent",
    attributes: .concurrent
)

concurrentQueue.async {
    print("1")
}
concurrentQueue.async {
    print("2")
}
concurrentQueue.async {
    print("3")
}
// Вывод: может быть 1,2,3 или 2,1,3 или 3,1,2 (случайный порядок)

Различия между Serial и Concurrent

// SERIAL — очень важно для data consistency
let dbQueue = DispatchQueue(label: "com.example.database")

var count = 0

dbQueue.async {
    count += 1  // Выполняется первым
}
dbQueue.async {
    count += 1  // Выполняется вторым
}
// count = 2 (всегда предсказуемо)

// CONCURRENT — может быть race condition
let concurrentQueue = DispatchQueue(
    label: "com.example.concurrent",
    attributes: .concurrent
)

var unsafeCount = 0

concurrentQueue.async {
    unsafeCount += 1  // Может конфликтовать
}
concurrentQueue.async {
    unsafeCount += 1  // С этим
}
// unsafeCount = 1 или 2? (непредсказуемо)

OperationQueue

// Высокоуровневая абстракция над GCD
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1  // Serial
queue.name = "com.example.operationQueue"
queue.qualityOfService = .userInitiated

// BlockOperation
let operation = BlockOperation {
    let data = fetchData()
    print(data)
}

operation.completionBlock = {
    print("Operation completed")
}

queue.addOperation(operation)

// Custom Operation
class DataFetchOperation: Operation {
    var data: [String] = []
    
    override func main() {
        if isCancelled { return }
        data = fetchDataFromAPI()
        if isCancelled { return }
        processData(data)
    }
}

let customOp = DataFetchOperation()
queue.addOperation(customOp)

Async/Await (Modern Swift)

// Вместо DispatchQueue используем async/await
func loadData() async {
    // Автоматически на background потоке
    let data = try await fetchData()
    
    // Явный переход на main для UI
    await MainActor.run {
        self.updateUI(with: data)
    }
}

// Или используя @MainActor
@MainActor
func updateUI(with data: Data) {
    self.label.text = data.description
}

Task {
    let data = try await loadData()
    await updateUI(with: data)  // Автоматически на main
}

Практические примеры

Пример 1: Network Request с UI update

func loadUserProfile() {
    // Запрос на background
    DispatchQueue.global(qos: .userInitiated).async {
        let user = self.fetchUser()  // Long operation
        
        // Update UI на main
        DispatchQueue.main.async {
            self.nameLabel.text = user.name
            self.avatarImage.image = user.avatar
        }
    }
}

// Лучше с async/await
func loadUserProfile() async {
    let user = await fetchUser()  // Background
    
    await MainActor.run {  // Main thread
        self.nameLabel.text = user.name
        self.avatarImage.image = user.avatar
    }
}

Пример 2: Database Operations (Serial)

class DatabaseManager {
    private let dbQueue = DispatchQueue(
        label: "com.example.database",
        attributes: []  // Serial
    )
    
    func saveUser(_ user: User) {
        dbQueue.async {
            // Всегда выполняется в порядке очереди
            let statement = "INSERT INTO users VALUES (...)"
            self.execute(statement)
        }
    }
    
    func getAllUsers() -> [User] {
        // Проблема: нужен sync для получения результата
        var result: [User] = []
        dbQueue.sync {
            result = self.query("SELECT * FROM users")
        }
        return result
    }
}

Пример 3: Image Processing (Concurrent)

let imageQueue = DispatchQueue(
    label: "com.example.image",
    attributes: .concurrent
)

func processImages(_ images: [UIImage]) {
    for image in images {
        imageQueue.async {
            let processed = applyFilter(image)
            DispatchQueue.main.async {
                self.displayImage(processed)
            }
        }
    }
}

Barrier в Concurrent Queues

// Для синхронизации в concurrent очереди
let concurrentQueue = DispatchQueue(
    label: "com.example.concurrent",
    attributes: .concurrent
)

var data: [String] = []

// Чтение
concurrentQueue.async {
    _ = data  // Может выполняться параллельно
}

// Запись с barrier
concurrentQueue.async(flags: .barrier) {
    data.append("new item")  // Выполняется исключительно
}

// Снова чтение
concurrentQueue.async {
    _ = data  // Гарантированно видит обновленные данные
}

Правила использования

Используй DispatchQueue когда: ✅ Нужна простая асинхронность
✅ Легкие задачи
✅ Нужна высокая производительность

Используй OperationQueue когда: ✅ Нужны зависимости между операциями
✅ Нужна отмена (cancellation)
✅ Нужны приоритеты

Используй async/await когда: ✅ Swift 5.5+
✅ Читаемость важнее
✅ Новый код

Выводы

  • Main Queue для UI
  • Global Queues для network/computations
  • Custom Serial для синхронизации данных
  • Custom Concurrent для параллельной обработки
  • async/await в новом коде вместо callbacks
Какие знаешь виды очередей? | PrepBro