The example is taken from the "A Tour of Go": https://tour.golang.org/concurrency/1
Obviously, program output should have 10 rows: 5 for "hello" and 5 for "world".
But we have on:
Linux output (9 rows):
$ go run 1.go
hello
world
hello
world
hello
world
world
hello
hello
MacOS X output (10 rows):
$ go run 1.go
hello
world
world
hello
hello
world
hello
world
hello
world
Can anyone explain - why?
Linux uname -a
:
Linux desktop 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt11-1 (2015-05-24) x86_64 GNU/Linux
MacOS X uname -a
:
Darwin 14.5.0 Darwin Kernel Version 14.5.0: Thu Jul 9 22:56:16 PDT 2015; root:xnu-2782.40.6~1/RELEASE_X86_64 x86_64
Source code from tour:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
From the specification:
Program execution begins by initializing the main package and then invoking the function
main
. When that function invocation returns, the program exits. It does not wait for other (non-main
) goroutines to complete.
So there is no guarantee that the goroutine printing "world"
will have time to complete before the program exits.
I suspect that if you run the program enough times, you will see both the 9-line and 10-line outputs on both platforms. Setting the GOMAXPROCS
environment variable to 2 might also help in triggering the problem.
You can fix it by making the main goroutine explicitly wait for completion of the other goroutine. For instance, using a channel:
func say(s string, done chan<- bool) {
for i := 0; i < 5; i++ {
time.Sleep(1000 * time.Millisecond)
fmt.Println(s)
}
done <- true
}
func main() {
c := make(chan bool, 2)
go say("world", c)
say("hello", c)
<-c
<-c
}
I've added a buffer to the channel so that the say
function can send a value without blocking (primarily so the "hello"
invocation actually returns). I then wait receive two values from the channel to make sure both invocations have completed.
For more complex programs, the sync.WaitGroup
type can provide a more convenient way to wait on multiple goroutines.