Have loook at this contrived example:
package main
import "fmt"
func printElo() {
fmt.Printf("Elo\n")
}
func printHello() {
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
go printElo()
go printHello()
i++
}
}
The output of this program would be just "This will print". Output of goroutines printElo()
and printHello
will not be emitted because, I guess, the main()
function thread will finish before the goroutines have a chance to even start executing.
What is the idiomatic way to make similar code work in Golang and not terminate prematurely?
Simplest, cleanest and "scalable" way to do it is to use a sync.WaitGroup
:
var wg = &sync.WaitGroup{}
func printElo() {
defer wg.Done()
fmt.Printf("Elo\n")
}
func printHello() {
defer wg.Done()
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i < 10 {
wg.Add(1)
go printElo()
wg.Add(1)
go printHello()
i++
}
wg.Wait()
}
Output (try it on the Go Playground):
This will print.Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Hello
Elo
Simple "rules" to follow when doing it with sync.WaitGroup
:
WaitGroup.Add()
in the "original" goroutine (that starts a new) before the go
statementWaitGroup.Done()
deferred, so it gets called even if the goroutine panicsWaitGroup
to other functions (and not use a package level variable), you must pass a pointer to it, else the WaitGroup
(which is a struct) would be copied, and the Done()
method called on the copy wouldn't be observed on the original