I have this simple program below
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
go func() {
fmt.Println("starting...")
time.Sleep(1 * time.Second)
fmt.Println("done....")
wg.Done()
} ()
wg.Wait()
}
Notice that I use var wg sync.WaitGroup
as a value, not a pointer. But the page for the sync package specifies that the Add
, Done
and Wait
function take a *sync.WaitGroup
.
Why/How does this work?
The method set of sync.WaitGroup
is the empty method set:
wg := sync.WaitGroup{}
fmt.Println(reflect.TypeOf(wg).NumMethod())
Output (try it on the Go Playground):
0
This is because all the methods of sync.WaitGroup
have pointer receivers, so they are all part of the method set of the *sync.WaitGroup
type.
When you do:
var wg sync.WaitGroup
wg.Add(1)
wg.Done()
// etc.
This is actually a shorthand for (&wg).Add(1)
, (&wg).Done()
etc.
This is in Spec: Calls:
If
x
is addressable and&x
's method set containsm
,x.m()
is shorthand for(&x).m()
.
So when you have a value that is addressable (a variable is addressable), you may call any methods that have pointer receiver on non-pointer values, and the compiler will automatically take the address and use that as the receiver value.
See related question:
Calling a method with a pointer receiver by an object instead of a pointer to it?
In your case you are modifying a global wg
object, if you pass it to a function you have to use pointer because you need to modify the object itself. If you pass by value, inside your function you will be modifying a copy of it, not the object itself.
From the documentation WaitGroup :
A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished.
From your Question
How does this work?
So for Add
method is set the number of goroutine your called. From your code you have only one :
func main() {
wg.Add(1)
go func() {
fmt.Println("starting...")
time.Sleep(1 * time.Second)
fmt.Println("done....")
wg.Done()
} ()
wg.Wait()
}
so it will tells goroutine to wait and print the result. as for wg.Done()
this is for telling the one gouroutine has finished to work. and it tells to the add
to decrement to -1
. You can see the code below How Done
method works :
// Done decrements the WaitGroup counter.
func (wg *WaitGroup) Done() {
wg.Add(-1)
}
and finally for the Wait
method this is for blocking goroutine until the WaitGroup counter is zero.
And another:
Why ?
from your code above if you don't use WaitGroup
you will not be able to print the result from the goroutine.
All in all you can read it in the documentation.