將共享變量的讀寫放到一個(gè) goroutine 中,其它 goroutine 通過(guò) channel 進(jìn)行讀寫操作所坯,這種方式有很多好處钦讳。
package bank
var deposits = make(chan int) // send amout 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 只在 teller 中可以訪問(wèn)
for {
select {
case amount := <-deposits:
balance += amount
case balances <- balance:
}
}
}
func init() {
go teller()
}
第二種就是最常見(jiàn)的互斥了银室,共享變量要互斥訪問(wèn)
可以用個(gè)數(shù)為 1 的信號(hào)量(semaphore)實(shí)現(xiàn)互斥
var (
sema = make(chan struct{}, 1)
balance int
)
func Deposit(amout int) {
sema <- struct{}{} // acquire token
balance = balance + amout
<-sema // release token
}
func Balance() int {
sema <- struct{}{} // acquire token
b := balance
<-sema // release token
return b
}
go 內(nèi)部提供了互斥操作
import "sync"
var (
mu sync.Mutex
balance int
)
func Deposit(amount int) {
mu.Lock()
defer mu.Unlock()
balance = balance + amount
}
func Balance() int {
mu.Lock()
defer mu.Unlock()
return balance
}
為什么 Balance 也需要互斥,多線程同時(shí)讀取一個(gè)變量有問(wèn)題嗎稻轨?灵莲?? Balance 也需要互斥是為了防止在寫的過(guò)程中進(jìn)行讀取殴俱。如果在讀的過(guò)程中沒(méi)有線程在寫多線程同時(shí)讀取是沒(méi)有問(wèn)題的政冻。所以為了提高讀取的并發(fā)量可以用讀寫鎖改寫
var mu sync.RWMutex
var balance int
func Balance() int {
mu.RLock() // readers lock
defer mu.RUnlock()
return balance
}
Deposit 不變,Balance 只獲取 RLock 讀鎖线欲,只要沒(méi)有線程在寫明场,多個(gè)線程可同時(shí)獲得 RLock 鎖。
Do not communicate by sharing memory. Instead, share memory by communicating.
sync 庫(kù)和 channel 的選擇要看場(chǎng)景李丰,哪種更簡(jiǎn)單更適合就用哪種苦锨。大部分場(chǎng)景可用 channel 實(shí)現(xiàn)。