概念簡介
在前麵的例子中,我們用互斥鎖進行了明確的鎖定來讓共享的
state 跨多個 Go語言 協程同步訪問。另一個選擇是使用內置的 Go語言
協程和通道的同步特性來達到同樣的效果。這個基於通道的方
法和 Go語言 通過通信來共享內存,以及確
保每塊數據被單獨的 Go語言 協程所擁有的思路是一致的。
例程代碼
package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
// 在這個例子中,state 將被一個單獨的 Go 協程擁有。這就
// 能夠保證數據在並行讀取時不會混亂。為了對 state 進行
// 讀取或者寫入,其他的 Go 協程將發送一條數據到擁有的 Go
// 協程中,然後接收對應的回複。結構體 `readOp` 和 `writeOp`
// 封裝這些請求,並且是擁有 Go 協程響應的一個方式。
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
// 和前麵一樣,我們將計算我們執行操作的次數。
var readOps uint64 = 0
var writeOps uint64 = 0
// `reads` 和 `writes` 通道分別將被其他 Go 協程用來發
// 布讀和寫請求。
reads := make(chan *readOp)
writes := make(chan *writeOp)
// 這個就是擁有 `state` 的那個 Go 協程,和前麵例子中的
// map一樣,不過這裏是被這個狀態協程私有的。這個 Go 協程
// 反複響應到達的請求。先響應到達的請求,然後返回一個值到
// 響應通道 `resp` 來表示操作成功(或者是 `reads` 中請求的值)
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
// 啟動 100 個 Go 協程通過 `reads` 通道發起對 state 所有者
// Go 協程的讀取請求。每個讀取請求需要構造一個 `readOp`,
// 發送它到 `reads` 通道中,並通過給定的 `resp` 通道接收
// 結果。
for r := 0; r < 100; r++ {
go func() {
for {
read := &readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
// 用相同的方法啟動 10 個寫操作。
for w := 0; w < 10; w++ {
go func() {
for {
write := &writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
// 讓 Go 協程們跑 1s。
time.Sleep(time.Second)
// 最後,獲取並報告 `ops` 值。
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
執行&輸出
# 運行這個程序顯示這個基於 Go 協程的狀態管理的例子達到
# 了每秒大約 800,000 次操作。
$ go run stateful-goroutines.go
readOps: 71708
writeOps: 7177
# 在這個特殊的例子中,基於 Go 協程的比基於互斥鎖的稍複雜。
# 這在某些例子中會有用,例如,在你有其他通道包含其中或者當你
# 管理多個這樣的互斥鎖容易出錯的時候。你應該使用最自然
# 的方法,特別是關於程序正確性的時候。
課程導航
學習上一篇:Go語言教程:互斥鎖 學習下一篇:Go語言教程:排序
相關資料
本例程github源代碼:https://github.com/xg-wang/gobyexample/tree/master/examples/stateful-goroutines