[Golang (Go)] Use sync.WaitGroup to wait for all the goroutines finish
sync.WaitGroup
WaitGroup
is used to wait for all the goroutines launched here to finish since the main function actually terminates before the goroutine gets a chance to execute by defatult.
WaitGroups essentially allow us to tackle this problem by blocking until any goroutines within that WaitGroup have successfully executed.
We first call .Add(n)
on our WaitGroup to set the number n of goroutines we want to wait for, and subsequently, we call .Done()
within any goroutine to signal the end of its’ execution.
Package sync
provides basic synchronization primitives such as mutual exclusion locks. Other than the Once
and WaitGroup
types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.
Examples
1 | // waitgroups.go |
Run it will output:
1 | go run waitgroups.go |
FAQs
The counter is negative
The value of the WaitGroup
counter must be greater than or equal to 0, otherwise a panic
error will occur.
In general, there are two scenarios that will cause the WaitGroup counter to be negative.
- Directly set the counter to a negative value through the
.Add()
function. For example, if you executeAdd(-1)
when it comes up, the program will panic and exit. This situation is relatively simple and the possibility of error is relatively low.
1 | func main() { |
- The
.Done()
function is executed too much, causing the counter to become negative. The correct way to useWaitGroup
is to first determine the value of the WaitGroup counter (the size of the coroutine group), and then call the.Done()
function the same number of times. If.Done()
is called too many times, the counter value will be negative and panic will occur.
1 | func main() { |
Of course, you can’t call it less, otherwise deadlock will occur.
1 | func main() { |
.Add()
execute after .Wait()
This question says that all .Add()
executions need to be ensured before .Wait()
is executed, otherwise, .Add()
has not been executed yet, .Wait()
is executed first, and if the counter value is equal to 0, proceed directly to the next step.
1 | func main() { |
Reuse previous WaitGroup whose .Wait()
is not there yet
The previous Wait() is not there yet, just reuse WaitGroup
WaitGroup can be reused. As long as the value of the WaitGroup counter becomes 0, it can be regarded as a newly created WaitGroup and be reused.
But before repeated use, you must wait until the previous Wait is completed before you can execute .Add()
or
.Done()
and other functions, otherwise it will report “unusable error”.
1 | func main() { |
References
[1] WaitGroup | sync - The Go Programming Language - https://golang.org/pkg/sync/#example_WaitGroup
[2] Go by Example: WaitGroups - https://gobyexample.com/waitgroups
[3] Go WaitGroup Tutorial | TutorialEdge.net - https://tutorialedge.net/golang/go-waitgroup-tutorial/