Ответ
Многопоточность — это техника, позволяющая приложению выполнять несколько задач (потоков) параллельно или псевдопараллельно (через быстрое переключение контекста). Это необходимо для:
- Отзывчивого UI (главный поток не блокируется долгими операциями).
- Эффективного использования многоядерных процессоров.
- Выполнения фоновых задач (сеть, вычисления, I/O).
Основные инструменты в iOS/macOS:
| Инструмент | Уровень | Основная концепция | Когда использовать |
|---|---|---|---|
| Grand Central Dispatch (GCD) | Низкоуровневый C API | Очереди (DispatchQueue) задач (closures). | Большинство задач: фоновая работа, синхронизация, таймеры. |
| OperationQueue | Высокоуровневый Objective-C/Swift API | Операции (Operation) — абстракция задачи с зависимостями, отменой, приоритетом. | Сложные задачи с зависимостями, требующие отмены или наблюдения за состоянием. |
| Thread (NSThread) | Самый низкий уровень | Напрямую управляемые потоки POSIX. | Редко. Только для специфичных, долгих задач, где нужен полный контроль над жизненным циклом потока. |
Базовый пример с GCD:
// 1. Выполнение задачи в фоне
DispatchQueue.global(qos: .userInitiated).async {
// Эта closure выполняется в фоновом потоке
let processedData = performIntensiveCalculation()
// 2. Возврат результата в главный поток для обновления UI
DispatchQueue.main.async {
self.updateUI(with: processedData)
}
}
Ключевые понятия и проблемы:
- Гонка данных (Race Condition): Несколько потоков одновременно обращаются и изменяют общие данные без синхронизации, приводя к неопределенному поведению.
- Решение: Использовать очереди (серийные или с барьерами), семафоры (
DispatchSemaphore), замки или акторы (Actor).
- Решение: Использовать очереди (серийные или с барьерами), семафоры (
- Взаимная блокировка (Deadlock): Два или более потока бесконечно ждут друг друга, освобождая ресурсы.
- Пример: Вызов
syncна текущей очереди.
- Пример: Вызов
- Главный поток (Main Thread): Весь пользовательский интерфейс (UI) должен обновляться только на главном потоке. Блокировка этого потока приводит к "зависанию" интерфейса.
Современный подход (Swift Concurrency): Начиная со Swift 5.5, рекомендуется использовать async/await, акторы (Actor) и структурированный параллелизм (Task, TaskGroup). Эти инструменты безопаснее и выразительнее, чем ручное управление потоками через GCD.
// Пример современного подхода
func fetchData() async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
Task { // Запускает асинхронную задачу
let data = try await fetchData() // Не блокирует поток
await MainActor.run { // Безопасный переход на главный актор
self.updateUI(with: data)
}
}