I'm surprised that go routines seem to interleave perfectly... After seeing this, I am starting to believe there's some missing information about the internals that I haven't learned about yet. Example:
$ go run x.go > output
$ grep ping output | wc -l
404778
$ grep pong output | wc -l
404777
$ cat x.go
package main
import (
"fmt"
"time"
)
type Ball struct{ hits int }
func main() {
table := make(chan *Ball)
go player("ping", table)
go player("pong", table)
table <- new(Ball) // game on; toss the ball
time.Sleep(1 * time.Second)
<-table // game over; grab the ball
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
//time.Sleep(1 * time.Millisecond)
table <- ball
}
}
However long you set the timeout in the player function (or remove it all together) you always get #ping == #ping +/- 1.
By default GOMAXPROCS is set to 1 and as a result you see this behaviour. If you increase GOMAXPROCS it will no longer be deterministic.
See this answer for an example
EDIT @DaveC disagrees however a simple test shows otherwise. The channels are synchonised yes, however the order of the goroutines executing is not. These are different concepts. Type in the above code, set GOMAXPROCS > 1 and run ...
➜ tmp export GOMAXPROCS=2
➜ tmp go run balls.go
ping 1
pong 2
➜ tmp go run balls.go
pong 1
ping 2
➜ tmp
As you can see above the goroutines order of execution is not deterministic
You're using a un-buffered channel and your two goroutines synchronize with it. With an un-buffered channel the channel write (table <- ball
) can only complete once someone somewhere has done a read (<-table
) so a single goroutine can never read the value it's writing. That's the whole point of this example.
According to this blog:
Goroutines would interleave perfectly for any number of players:
The answer is because Go runtime holds waiting FIFO queue for receivers (goroutines ready to receive on the particular channel), and in our case every player gets ready just after he passed the ball on the table