在前面的範例中,我們使用顯式鎖定互斥體來同步對多個goroutine的共用狀態的存取。 另一個選項是使用goroutine
和通道的內建同步功能來實現相同的結果。這種基於通道的方法與Go的共用記憶體的想法一致,通過溝通,擁有每個資料的goroutine
恰好只有1
個。
在這個例子中,狀態將由單個goroutine
擁有。這將保證資料不會因併行存取而損壞。為了讀或寫狀態,其他goroutine
將傳送訊息到擁有的goroutine
並接收相應的回復。這些readOp
和writeOp
結構封裝了這些請求,並擁有一個goroutine
響應的方法。
和以前一樣,我們將計算執行的運算元。
讀寫通道將被其他goroutine
分別用來發出讀和寫請求。
這裡是擁有狀態的goroutine
,它是一個如前面範例中的對映,但現在對狀態goroutine
是私有的。這個goroutine
在讀取和寫入通道時重複選擇,在請求到達時響應請求。 通過首先執行所請求的操作,然後在響應通道上傳送值以指示成功(以及在讀取的情況下的期望值)來執行響應。
這裡啟動了100
個goroutine
來通過讀取通道向狀態擁有的goroutine
發出讀取。每次讀取都需要構造一個readOp
,通過讀取通道傳送readOp
,並通過提供的resp
通道接收結果。
也使用類似的方法開始10
個寫操作。讓goroutine
工作一秒鐘。最後,捕獲和報告操作計數。
執行程式顯示,基於goroutine
的狀態管理範例程式,完成了大約80,000
次操作。
對於這種特殊情況,基於goroutine
的方法比基於互斥的方法更多一些。它在某些情況下可能是有用的,例如,當有其他通道涉及或管理多個此類互斥體將容易出錯。應該使用最自然的方法,有助於理解程式。
所有的範例程式碼,都放在
F:\worksp\golang
目錄下。安裝Go程式設計環境請參考:/2/23/798.html
stateful-goroutines.go
的完整程式碼如下所示 -
package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
// In this example our state will be owned by a single
// goroutine. This will guarantee that the data is never
// corrupted with concurrent access. In order to read or
// write that state, other goroutines will send messages
// to the owning goroutine and receive corresponding
// replies. These `readOp` and `writeOp` `struct`s
// encapsulate those requests and a way for the owning
// goroutine to respond.
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
// As before we'll count how many operations we perform.
var readOps uint64 = 0
var writeOps uint64 = 0
// The `reads` and `writes` channels will be used by
// other goroutines to issue read and write requests,
// respectively.
reads := make(chan *readOp)
writes := make(chan *writeOp)
// Here is the goroutine that owns the `state`, which
// is a map as in the previous example but now private
// to the stateful goroutine. This goroutine repeatedly
// selects on the `reads` and `writes` channels,
// responding to requests as they arrive. A response
// is executed by first performing the requested
// operation and then sending a value on the response
// channel `resp` to indicate success (and the desired
// value in the case of `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
}
}
}()
// This starts 100 goroutines to issue reads to the
// state-owning goroutine via the `reads` channel.
// Each read requires constructing a `readOp`, sending
// it over the `reads` channel, and the receiving the
// result over the provided `resp` channel.
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)
}
}()
}
// We start 10 writes as well, using a similar
// approach.
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)
}
}()
}
// Let the goroutines work for a second.
time.Sleep(time.Second)
// Finally, capture and report the op counts.
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
執行上面程式碼,將得到以下輸出結果 -
F:\worksp\golang>go run mutexes.go
readOps: 84546
writeOps: 8473
state: map[0:99 3:3 4:62 1:18 2:89]