Go有狀態的goroutines實例
在前面的示例中,我們使用顯式鎖定互斥體來同步對多個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編程環境請參考:http://www.yiibai.com/go/go\_environment.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]