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

Что такое DispatchGroup?

1.2 Junior🔥 111 комментариев
#Многопоточность и асинхронность

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

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

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

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 — код становится чище и безопаснее.