[go async] WaitGroup
Intro
As we know, according of a tour of Go, a goroutine is a lightweight thread managed by the Go runtime.
One of a common problems with these goroutines - waiting for ending of all children goroutines in a main goroutine before end.
Look on this code that simulate same useful work:
package main
import (
"fmt"
"time"
)
func consumer1() {
fmt.Println("[consumer 1] Strat some work")
time.Sleep(2 * time.Second)
fmt.Println("[consumer 1] End some work")
}
func consumer2() {
fmt.Println("[consumer 2] Strat some work")
time.Sleep(1 * time.Second)
fmt.Println("[consumer 2] End some work")
}
func runConsumers() {
go consumer1()
go consumer2()
}
func main() {
runConsumers()
}
Try to run it and your got an empty result:
go run main.go
It’s happened because a main goroutine end of work before children goroutines end of own works.
WaitGroup
For solver this problem, Go has a standard library primitive WaitGroup
from package sync.
How it work
The main goal of whit primitive is:
- increase goroutine counter while your adding new goroutine;
- decrease goroutine counter when goroutine done his job;
- and blocks execution as long as this counter is greater than zero.
Three method that WaitGroup
has for done this job:
- Add(int): increases
WaitGroup
goroutine count by given integer value. - Done(): decreases
WaitGroup
goroutine counter by1
, that indicate that termination of a goroutine. - Wait(): block execution as long as the
WaitGroup
goroutine counter is greater than zero.
Example
Let’s use WaitGroup
for modify our example:
package main
import (
"fmt"
"sync"
"time"
)
func consumer1(wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("[consumer 1] Strat some work")
time.Sleep(2 * time.Second)
fmt.Println("[consumer 1] End some work")
}
func consumer2(wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("[consumer 2] Strat some work")
time.Sleep(1 * time.Second)
fmt.Println("[consumer 2] End some work")
}
func runConsumers() {
wg := new(sync.WaitGroup)
// Add 2 goroutine
wg.Add(2)
// Run goroutines
go consumer1(wg)
go consumer2(wg)
// Waiting for counter=0
wg.Wait()
}
func main() {
runConsumers()
}
Try to run it - we got result:
go run main.go
[consumer 2] Strat some work
[consumer 1] Strat some work
[consumer 2] End some work
[consumer 1] End some work
Conclusions
Use sync.WaitGroup
if you need to run children goroutines and wait in main goroutine until children goroutines will done own job.