Ref: https://tour.golang.org/concurrency/11
Channel
Channels are great for communication among goroutines.
By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.
The code is using Golang channel technique to check if the in-order transversal sequences of two binary tree are the same. Notice that in order to use range
to receive data from channel, we must close
the channel on the sender side after all sending jobs are done, otherwise it will cause a dead lock.
package main
import (
"golang.org/x/tour/tree"
"fmt"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
WalkTree(t, ch)
close(ch)
}
func WalkTree(t *tree.Tree, ch chan int) {
if t == nil {
return
}
WalkTree(t.Left, ch)
ch <- t.Value
WalkTree(t.Right, ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
c1 := make(chan int)
c2 := make(chan int)
go Walk(t1, c1)
go Walk(t2, c2)
for i := range c1 {
if i != <-c2 {
return false
}
}
return true
}
func main() {
fmt.Println(Same(tree.New(1), tree.New(1)))
}
Daisy chain example
Rob Pike gave following example at Google I/O 2012. https://www.youtube.com/watch?v=f6kdp27TYZs
package main
import "fmt"
func pass(left, right chan int){
left <- 1 + <- right
}
func main(){
const n = 50
leftmost := make(chan int)
right := leftmost
left := leftmost
for i := 0; i< n; i++ {
right = make(chan int)
// the chain is constructed from the end
go pass(left, right) // the first goroutine holds (leftmost, new chan)
left = right // the second and following goroutines hold (last right chan, new chan)
}
go func(c chan int){ c <- 1}(right)
fmt.Println("sum:", <- leftmost)
}
Mutex
Mutual exclusion to make sure only one goroutine can access a variable at a time to avoid conflicts. The conventional name for the data structure that provides it is mutex.
Go’s standard library provides mutual exclusion with sync.Mutex and its two methods:
Lock
Unlock
We can define a block of code to be executed in mutual exclusion by surrounding it with a call to
Lock
andUnlock
as shown on theInc
method.
We can also use defer to ensure the mutex will be unlocked as in the
Value
method.
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mux.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}