For the past few weeks I've been wrestling with one (not-so) simple question:
When is it best to use a sync.Mutex
and, conversely, when is it best use a chan
?
It seems that for a lot of problems either strategy is interchangeable with the other - and that's just the problem!
Take this video found in the Golang documentation. Below, I've taken the liberty to dictate the code in the playground and also translate it to a sync.Mutex
equivalent.
Is there a certain problem - encountered in the real world - that warrants the use of one other?
Notes:
- I am a huge fan of this use of chan and struggle to think of a more elegant implementation using sync.Mutex.
- It's worth noting that the
chan
implementation does more work in the same time (reaches 12)*
Playgrounds:
Ping/pong with chan
:
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)
time.Sleep(1 * time.Second)
<-table
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
time.Sleep(100 * time.Millisecond)
table <- ball
}
}
Ping/pong with sync.Mutex
:
package main
import (
"fmt"
"time"
"sync"
)
type Ball struct { hits int }
var m = sync.Mutex{}
func main() {
ball := new(Ball)
go player("ping", ball)
go player("pong", ball)
time.Sleep(1 * time.Second)
}
func player(name string, ball *Ball) {
for {
m.Lock()
ball.hits++
fmt.Println(name, ball.hits)
time.Sleep(100 * time.Millisecond)
m.Unlock()
}
}