當前位置: 首頁>>編程語言>>正文


Go語言教程:狀態協程

返回Go語言教程首頁

概念簡介

在前麵的例子中,我們用互斥鎖進行了明確的鎖定來讓共享的
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

Go語言狀態協程

本文由《純淨天空》出品。文章地址: https://vimsky.com/zh-tw/article/4074.html,未經允許,請勿轉載。