# Go 互斥
上面的例子中,我們看過了如何在多個協程之間原子地訪問計數器,對于更復雜的例子,我們可以使用`Mutex`來在多個協程之間安全地訪問數據。
```go
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
)
func main() {
// 這個例子的狀態就是一個map
var state = make(map[int]int)
// 這個`mutex`將同步對狀態的訪問
var mutex = &sync.Mutex{}
// ops將對狀態的操作進行計數
var ops int64 = 0
// 這里我們啟動100個協程來不斷地讀取這個狀態
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
// 對于每次讀取,我們選取一個key來訪問,
// mutex的`Lock`函數用來保證對狀態的
// 唯一性訪問,訪問結束后,使用`Unlock`
// 來解鎖,然后增加ops計數器
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddInt64(&ops, 1)
// 為了保證這個協程不會讓調度器出于饑餓狀態,
// 我們顯式地使用`runtime.Gosched`釋放了資源
// 控制權,這種控制權會在通道操作結束或者
// time.Sleep結束后自動釋放。但是這里我們需要
// 手動地釋放資源控制權
runtime.Gosched()
}
}()
}
// 同樣我們使用10個協程來模擬寫狀態
for w := 0; w < 10; w++ {
go func() {
for {
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddInt64(&ops, 1)
runtime.Gosched()
}
}()
}
// 主協程Sleep,讓那10個協程能夠運行一段時間
time.Sleep(time.Second)
// 輸出總操作次數
opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
// 最后鎖定并輸出狀態
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
```
運行結果
```go
ops: 3931611
state: map[0:84 2:20 3:18 1:65 4:31]
```
- 版權
- 內容
- Go常量
- Go變量
- Go 數值
- Go 數組
- Go 字典
- Go 函數定義
- Go 方法
- Go 結構體
- Go 閉包函數
- Go 接口
- Go 字符串操作函數
- Go 字符串格式化
- Go 自定義排序
- Go Base64編碼
- Go Defer
- Go Exit.md
- Go for循環
- Go if..else if..else 條件判斷
- Go JSON支持
- Go Line Filters
- Go 狀態協程
- Go Panic
- Go range函數
- Go SHA1 散列
- Go String與Byte切片之間的轉換
- Go Switch語句
- Go URL解析
- Go 遍歷通道
- Go 并行功能
- Go 并行通道Channel
- Go 超時
- Go 錯誤處理
- Go 打點器
- Go 遞歸函數
- Go 讀取文件
- Go 工作池
- Go 關閉通道
- Go 函數多返回值
- Go 函數回調
- Go 函數命名返回值
- Go 互斥
- Go 環境變量
- Go 集合功能
- Go 計時器
- Go 進程觸發
- Go 進程執行
- Go hello world
- Go 可變長參數列表
- Go 命令行參數
- Go 命令行參數標記
- Go 排序
- Go 切片
- Go 請求處理頻率控制
- Go 時間
- Go 時間戳
- Go 時間格式化和解析
- Go 數字解析
- Go 隨機數
- Go 通道的同步功能
- Go 通道方向
- Go 通道緩沖
- Go 通道選擇Select
- Go 寫入文件
- Go 信號處理
- Go 原子計數器
- Go 正則表達式
- Go 指針