🤔 Зачем нужны WaitGroup?



В Go sync.WaitGroup используется для синхронизации выполнения горутин. Она позволяет основной горутине (или любой другой горутине) ждать завершения группы горутин перед продолжением работы. Это особенно полезно, когда нужно убедиться, что все фоновые задачи завершены до выполнения дальнейших действий.



🚩Основные функции WaitGroup



🟠Add(delta int)

Увеличивает (или уменьшает) счетчик горутин на заданное значение delta.

Обычно вызывается до запуска горутин, чтобы установить количество горутин, которые нужно дождаться.

🟠Done()

Уменьшает счетчик горутин на 1.

Вызывается горутиной, когда она завершает свою работу.

🟠Wait()

Блокирует выполнение до тех пор, пока счетчик горутин не станет равен нулю.

Обычно вызывается основной горутиной для ожидания завершения всех горутин.



🚩Пример использования `WaitGroup`



Мы используем WaitGroup для ожидания завершения нескольких горутин.

package main



import (

"fmt"

"sync"

"time"

)



func worker(id int, wg *sync.WaitGroup) {

defer wg.Done() // Уменьшает счетчик на 1 при завершении работы горутины

fmt.Printf("Worker %d starting\n", id)

time.Sleep(time.Second)

fmt.Printf("Worker %d done\n", id)

}



func main() {

var wg sync.WaitGroup



for i := 1; i <= 5; i++ {

wg.Add(1) // Увеличивает счетчик горутин на 1

go worker(i, &wg)

}



wg.Wait() // Ожидает завершения всех горутин

fmt.Println("All workers done")

}




🟠Мы создаем 5 горутин, каждая из которых выполняет функцию worker.

🟠Счетчик WaitGroup увеличивается на 1 перед запуском каждой горутины с помощью wg.Add(1).

🟠Каждая горутина вызывает wg.Done() при завершении, уменьшая счетчик на 1.

🟠Основная горутина вызывает wg.Wait(), блокируясь до тех пор, пока все горутины не завершат свою работу.



🚩Почему `WaitGroup` необходимы



🟠Синхронизация выполнения

Позволяет основной горутине дождаться завершения всех запущенных горутин, что особенно важно для корректного завершения программы или выполнения зависимых задач.

🟠Избежание дедлоков

Гарантирует, что основная горутина не завершит выполнение программы до того, как завершатся все горутины, предотвращая возможные дедлоки или незавершенные операции.

🟠Упрощение управления горутинами

Позволяет легко управлять множеством горутин, не требуя сложной логики для отслеживания их завершения.



🚩Пример с ошибкой без `WaitGroup`



Без использования WaitGroup основной поток может завершиться до завершения всех горутин, что приведет к неполной обработке данных. В этом примере использование time.Sleep для ожидания является ненадежным и не гарантирует завершение всех горутин. Вместо этого правильное использование WaitGroup обеспечивает корректное завершение всех задач.

package main



import (

"fmt"

"time"

)



func worker(id int) {

fmt.Printf("Worker %d starting\n", id)

time.Sleep(time.Second)

fmt.Printf("Worker %d done\n", id)

}



func main() {

for i := 1; i <= 5; i++ {

go worker(i)

}



time.Sleep(2 * time.Second) // Это не гарантирует завершение всех горутин

fmt.Println("All workers done")

}




Ставь 👍 и забирай 📚 Базу знаний