AI编程 · 架构思考 · 技术人生

Go语言锁机制深度指南

智谱 GLM,支持多语言、多任务推理。从写作到代码生成,从搜索到知识问答,AI 生产力的中国解法。

Go语言锁机制深度指南

一、并发与并行

并发(Concurrency):同时处理多个任务
并行(Parallelism):同时执行多个任务

Go的并发模型:
Goroutines:轻量级线程
Channels:通信管道

二、为什么需要锁

数据竞争

多个goroutine同时访问共享数据,至少一个是写操作,就会产生数据竞争。

问题:结果不可预测

解决:用锁保护共享数据

锁的核心操作

lock.Lock()      // 获取锁
// 临界区代码
lock.Unlock()    // 释放锁

三、sync.Mutex:互斥锁

基本用法

import "sync"

var mu sync.Mutex
var count int

func increment() {
    mu.Lock()
    count++
    mu.Unlock()
}

TryLock(Go 1.18+)

非阻塞获取锁:

if mu.TryLock() {
    // 获取成功,执行操作
    defer mu.Unlock()
} else {
    // 获取失败,处理其他逻辑
}

检测数据竞争

go run -race main.go

四、sync.RWMutex:读写锁

适用于读多写少的场景。

API说明

方法 说明
RLock/RUnlock 获取/释放读锁(多个读可以同时持有)
Lock/Unlock 获取/释放写锁(独占)

使用示例

var rw sync.RWMutex
var data map[string]string

func read(key string) string {
    rw.RLock()
    defer rw.RUnlock()
    return data[key]
}

func write(key, value string) {
    rw.Lock()
    defer rw.Unlock()
    data[key] = value
}

何时选择RWMutex

  • 读操作远多于写操作
  • 读操作耗时较长
  • 需要并发读取

五、常见陷阱

1. 死锁

// ❌ 错误:重复加锁
mu.Lock()
mu.Lock()  // 死锁!
// ❌ 错误:锁顺序不一致
// goroutine 1
mu1.Lock()
mu2.Lock()

// goroutine 2
mu2.Lock()
mu1.Lock()  // 可能死锁

2. 忘记Unlock

// ❌ 危险
mu.Lock()
if someCondition {
    return  // 忘记解锁!
}
mu.Unlock()
// ✅ 正确:用defer
mu.Lock()
defer mu.Unlock()
if someCondition {
    return
}

3. 临界区范围不当

// ❌ 临界区太大
mu.Lock()
data = fetchFromDB()  // I/O操作在锁内!
mu.Unlock()
// ✅ 正确:临界区只保护共享数据
temp := fetchFromDB()
mu.Lock()
data = temp
mu.Unlock()

六、最佳实践

1. 保持临界区简短

锁内只做必要的共享数据操作。

2. 选择合适的锁粒度

粒度 优点 缺点
粗粒度 简单,不易出错 并发度低
细粒度 并发度高 复杂,易死锁

3. 在结构体中嵌入锁

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value++
}

4. 避免在持有锁时阻塞

不要在锁内进行:
– 网络I/O
– 文件I/O
– 通道操作(可能阻塞)
– time.Sleep

七、其他同步原语

sync/atomic

适用于简单的计数器、标志位:

import "sync/atomic"

var count int64
atomic.AddInt64(&count, 1)

Channels

Go推荐的方式:通过通信共享内存

ch := make(chan int, 1)
ch <- value  // 发送
v := <-ch    // 接收

sync.WaitGroup

等待一组goroutine完成:

var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        // 工作
    }()
}
wg.Wait()

八、性能分析

用pprof分析锁竞争:

import _ "net/http/pprof"

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()
    // 应用代码
}

访问:http://localhost:6060/debug/pprof/

九、总结

锁类型 适用场景
sync.Mutex 通用互斥锁
sync.RWMutex 读多写少
sync/atomic 简单计数器
Channel 通信同步

核心原则
1. 临界区尽可能小
2. 用defer避免忘记解锁
3. 统一加锁顺序避免死锁
4. 优先用Channel通信


参考链接
– 原文:https://docs.80aj.com/docs/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Go%20%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E9%94%81%E6%9C%BA%E5%88%B6.html

赞(0)
未经允许不得转载:Toy's Tech Notes » Go语言锁机制深度指南
免费、开放、可编程的智能路由方案,让你的服务随时随地在线。

评论 抢沙发

十年稳如初 — LocVPS,用时间证明实力

10+ 年老牌云主机服务商,全球机房覆盖,性能稳定、价格厚道。

老品牌,更懂稳定的价值你的第一台云服务器,从 LocVPS 开始