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?
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.
A common usage of WaitGroup is to block the main function because we know that the main function itself is also a GoRoutine. You need to import the sync package before you can use WaitGroup.
Sync Package To The Rescue The right way to implement a singleton pattern in Go is to use the sync package's Once.Do() function. This function makes sure that your specified code is executed only once and never more than once. The way to use the Once.Do() function is as below.
WaitGroup is actually a type of counter which blocks the execution of function (or might say A goroutine) until its internal counter become 0. How It Works ? WaitGroup exports 3 methods.
In the code above, we are first using the add function which tells the waitgroup how many goroutines to block for. Then we simply pass the group as a pointer to a goroutine. When the work is done by the goroutine we call the Done method that tells the waitgroup to stop blocking.
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. This is actually a shorthand for (&wg).Add (1), (&wg).Done () etc. If x is addressable and &x 's method set contains m, x.m () is shorthand for (&x).m ().
Waitgroup is a blocking mechanism that blocks when none of the goroutines which is inside that group has executed. If a goroutine is finished it then unblocks the group. Here is an example illustrating how to use a waitgroup with goroutine. fmt.Println ("Working...") fmt.Println ("Done working!")
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With