Что такое DispatchGroup?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
DispatchGroup — координация выполнения нескольких асинхронных задач
DispatchGroup — это механизм GCD, который позволяет координировать выполнение нескольких асинхронных операций и ждать, пока все они не завершатся. Это необходимо, когда нужно убедиться, что несколько параллельных операций закончились, прежде чем продолжить.
Базовое использование DispatchGroup
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
print("Task 1 started")
sleep(2)
print("Task 1 finished")
}
DispatchQueue.global().async(group: group) {
print("Task 2 started")
sleep(3)
print("Task 2 finished")
}
DispatchQueue.global().async(group: group) {
print("Task 3 started")
sleep(1)
print("Task 3 finished")
}
group.notify(queue: .main) {
print("All tasks completed!")
// Обновляем UI
}
Вывод:
Task 1 started
Task 2 started
Task 3 started
Task 3 finished
Task 1 finished
Task 2 finished
All tasks completed!
Ожидание с timeout
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
sleep(5)
}
let result = group.wait(timeout: .now() + 2) // Ждём максимум 2 секунды
if result == .timedOut {
print("Tasks took too long!")
} else {
print("All tasks completed in time!")
}
Блокирующее ожидание (осторожно!)
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
let data = fetchDataFromServer()
self.data = data
}
group.wait() // Блокирует текущий поток
// Теперь данные загружены
ВАЖНО: Никогда не вызывай group.wait() в главном потоке! Зависнет UI:
// ❌ DEADLOCK!
DispatchQueue.main.async {
group.wait() // Главный поток ждёт, а задачи не могут выполниться
}
Практический пример: загрузка нескольких данных
class DataViewModel {
@Published var users: [User] = []
@Published var posts: [Post] = []
@Published var comments: [Comment] = []
@Published var isLoading = false
func loadAllData() {
isLoading = true
let group = DispatchGroup()
// Загружаем пользователей
group.enter()
DispatchQueue.global().async {
let users = self.fetchUsers()
DispatchQueue.main.async {
self.users = users
group.leave()
}
}
// Загружаем посты
group.enter()
DispatchQueue.global().async {
let posts = self.fetchPosts()
DispatchQueue.main.async {
self.posts = posts
group.leave()
}
}
// Загружаем комментарии
group.enter()
DispatchQueue.global().async {
let comments = self.fetchComments()
DispatchQueue.main.async {
self.comments = comments
group.leave()
}
}
// Ждём, когда всё загрузится
group.notify(queue: .main) {
self.isLoading = false
print("All data loaded!")
}
}
}
Enter и Leave методы
let group = DispatchGroup()
// Вручную управляем счётчиком группы
group.enter() // Счётчик: 0 → 1
DispatchQueue.global().async {
// Долгая операция
sleep(2)
group.leave() // Счётчик: 1 → 0
}
group.enter() // Счётчик: 0 → 1
DispatchQueue.global().async {
sleep(1)
group.leave() // Счётчик: 1 → 0
}
group.notify(queue: .main) {
// Вызовется когда счётчик вернётся в 0
print("All done!")
}
Обработка ошибок с DispatchGroup
class APIManager {
func fetchMultipleResources(completion: @escaping (Result<[Data], Error>) -> Void) {
let group = DispatchGroup()
var results: [Data] = []
var errors: [Error] = []
for id in 1...5 {
group.enter()
fetchResource(id: id) { result in
switch result {
case .success(let data):
results.append(data)
case .failure(let error):
errors.append(error)
}
group.leave()
}
}
group.notify(queue: .main) {
if !errors.isEmpty {
completion(.failure(errors.first!))
} else {
completion(.success(results))
}
}
}
}
Преобразование callbacks в async/await
func loadAllDataAsync() async {
let group = DispatchGroup()
var data: [String: Any] = [:]
return await withTaskGroup(of: Void.self) { taskGroup in
taskGroup.addTask {
let users = await self.fetchUsersAsync()
data["users"] = users
}
taskGroup.addTask {
let posts = await self.fetchPostsAsync()
data["posts"] = posts
}
// Ждём все задачи
await taskGroup.waitForAll()
}
}
Сравнение DispatchGroup с async/await
// DispatchGroup (старый подход)
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// work
group.leave()
}
group.notify(queue: .main) {
// callback
}
// async/await (современный подход)
async {
await work() // Чище и понятнее
}
Ключевые правила
✅ Используй async(group:) когда добавляешь в DispatchQueue
✅ Используй enter() и leave() для ручного управления
✅ Всегда вызови notify() в главном потоке для UI обновления
✅ Используй timeout для длительных операций
❌ Никогда не блокируй главный поток с wait()
❌ Не забывай вызвать leave() для каждого enter()
Альтернатива: Для новых проектов рекомендуется использовать Swift async/await и TaskGroup вместо DispatchGroup — код становится чище и безопаснее.