// Alice
go func() {
bank.Deposit(200) // A1
fmt.Println("=", bank.Balance()) // A2
}()
// Bob
go bank.Deposit(100) // B
Alice first Bob first Alice/Bob/Alice
0 0 0
A1 200 B 100 A1 200
A2 "= 200" A1 300 B 300
B 300 A2 "= 300" A2 "= 300"
Goroutine Monitor
Do not communicate by sharing memory; instead, share memory by communicating.
package bank // Package bank provides a concurrency-safe bank with one account.
var deposits = make(chan int) // send amount to deposit
var balances = make(chan int) // receive balance
func Deposit(amount int) { deposits <- amount }
func Balance() int { return <-balances }
func teller() {
var balance int // balance is confined to teller goroutine
for {
select {
case amount := <-deposits:
balance += amount
case balances <- balance:
}
}
}
func init() {
go teller() // start the monitor goroutine
}
Mutual Exclusion
Remember the concurrent web crawler example? we may use a buffered channel as a counting semaphore to ensure that no more than 20 goroutines made simultaneous HTTP requests.
A semaphore that counts only to 1 is called a binary semaphore.
var (
sema = make(chan struct{}, 1) // a binary semaphore guarding balance
balance int
)
func Deposit(amount int) {
sema <- struct{}{} // acquire token
balance = balance + amount
<-sema // release token
}
func Balance() int {
sema <- struct{}{} // acquire token
b := balance
<-sema // release token
return b
}
Mutual Exclusion with Mutexes
import "sync"
var (
mu sync.Mutex // guards balance
balance int
)