# 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 指针