@[TOC](sync.metux 使用)
简介
简述使用metux使用的方法, 使用的注意点, 以及使用情况
使用方法
提供的方法
Lock() 方法用于获取锁
Unlock() 方法用于释放锁
TryLock()方法尝试获取锁
对共享资源进行加锁, 例 :
go
func increment() {
// 加锁
mutex.Lock()
// 解锁
defer mutex.Unlock()
// 访问共享资源
counter++
fmt.Printf("Incremented: %d\n", counter)
// 模拟一些工作
time.Sleep(time.Millisecond * 100)
}
func main() {
var wg sync.WaitGroup
// 启动多个 goroutine 来模拟并发访问
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
// 等待所有 goroutine 完成
wg.Wait()
fmt.Println("Final Counter:", counter)
}
Incremented: 1
Incremented: 2
Incremented: 3
Incremented: 4
Incremented: 5
Final Counter: 5
使用注意点
1、锁不可复制 , 例如需要用func 传递 metux 。 源码中锁结构中主要是一个state(记录锁的状态), 本来锁只有一把, 一个协程拿到一个锁, 如果复制了 那 俩把锁共享一个内存状态, 那就出错了。
go
// 错误使用
func deal(m sync.Mutex) {
...
}
// 正确做法
func deal(m *sync.Mutex) {
...
}
2、 缩短、小锁使用的范围
go
// 不建议
mutex.Lock()
// 其他逻辑处理
counter++
// 其他逻辑处理
mutex.Unlock()
// 建议
mutex.Lock()
counter++
mutex.Unlock()
当然不能一概而论
go
// 如果循环体内对共享资源的访问非常简单,而且锁的粒度很小,可能在循环内加锁更为合适。这样可以更细粒度地控制对共享资源的访问,减小锁的持有时间
for i := 0; i < 10; i++ {
mutex.Lock()
// 对共享资源的简单访问
mutex.Unlock()
}
// 如果循环体内有复杂的逻辑,而且锁的粒度比较大,可能需要在循环外加锁,以避免在每次迭代时频繁地获取和释放锁,从而降低性能
mutex.Lock()
defer mutex.Unlock()
for i := 0; i < 10; i++ {
// 复杂逻辑,可能涉及多个步骤
}
3、避免锁嵌套 可能会造成死锁的情况
例子是 《Concurrency in GO》-- 死锁一章
4、考虑使用读写锁, 在读多写少的情况下
5、如果想要锁用来通讯, 建议使用channel。 锁主要是保护共享资源的访问的。
使用场景
1、共享资源的访问
2、确保某个操作是原子的
这张图 也是 《Concurrency in GO》-并发哲学, 建议我们什么时候使用锁, 什么时候使用channel
源码解读