Mutex
我们已经看到通道在 goroutine 之间通信方面非常出色。
但如果我们不需要通信呢?如果我们只是想确保同一时间只有一个 goroutine 能访问某个变量,以避免冲突呢?
这个概念称为 互斥 ,而提供它的数据结构的惯用名称是互斥锁 。
Go标准库通过sync.Mutex及其两个方法(Lock、Unlock)提供互斥锁。
一般将Mutex写在结构体里面:
go
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}Mutex锁的作用域实际上并不是以结构体为单位的,它的保护范围是上锁和解锁代码之间访问到的数据。
具体实现规则是:一旦有一个协程执行lock后,后续代码访问到的数据都会被上锁,同一时间其他也要访问这些数据并执行lock的协程会被阻塞,直到unlock被执行。
示例
go
var wait sync.WaitGroup
var count = 0
var lock sync.Mutex
func main() {
wait.Add(10)
for i := 0; i < 10; i++ {
go func(data *int) {
// 加锁
lock.Lock()
// 模拟访问耗时
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
// 访问数据
temp := *data
// 模拟计算耗时
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
ans := 1
// 修改数据
*data = temp + ans
// 解锁
lock.Unlock()
fmt.Println(*data)
wait.Done()
}(&count)
}
wait.Wait()
fmt.Println("最终结果", count)
}读写锁
读写互斥锁sync.RWMutex
- 如果获得了读锁,其他协程进行写操作时会阻塞,其他协程进行读操作时不会阻塞
- 如果获得了写锁,其他协程进行写操作时会阻塞,其他协程进行读操作时会阻塞
go
// 加读锁
func (rw *RWMutex) RLock()
// 尝试加读锁
func (rw *RWMutex) TryRLock() bool
// 解读锁
func (rw *RWMutex) RUnlock()
// 加写锁
func (rw *RWMutex) Lock()
// 尝试加写锁
func (rw *RWMutex) TryLock() bool
// 解写锁
func (rw *RWMutex) Unlock()更安全的写法
go
c.mu.Lock()
defer c.mu.Unlock() // 确保函数返回/panic 时一定释放锁
c.v[key]++