使用sync.Mutex可避免多goroutine并发修改共享变量导致的数据竞争,通过加锁保护临界区,确保同一时间只有一个goroutine能访问共享资源。

在Go语言中,sync.Mutex 是最常用的同步原语之一,用于保护共享资源不被多个goroutine同时访问。下面通过一个简单但典型的示例,展示如何正确使用 sync.Mutex 来避免竞态条件。
问题场景:并发修改共享变量
假设多个goroutine同时对一个全局计数器进行递增操作,如果不加保护,会导致数据竞争:
var counter int
func worker() {
for i := 0; i < 1000; i++ {
counter++ // 非原子操作,存在竞态
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
worker()
wg.Done()
}()
}
wg.Wait()
fmt.Println("最终计数:", counter) // 结果可能小于预期的5000
}
使用 Mutex 保护临界区
通过引入 *sync.Mutex,可以确保同一时间只有一个goroutine能进入临界区:
var (
counter int
mu sync.Mutex
)
func safeWorker() {
for i := 0; i < 1000; i++ {
mu.Lock()
counter++
mu.Unlock()
}
}
每次修改 counter 前先调用 Lock(),修改完成后立即调用 Unlock(),保证操作的原子性。
立即学习“go语言免费学习笔记(深入)”;
更安全的写法:配合 defer 使用
为防止忘记解锁或在锁持有期间发生 panic 导致死锁,推荐使用 defer mu.Unlock():
func saferWorker() {
for i := 0; i < 1000; i++ {
mu.Lock()
defer mu.Unlock()
counter++
}
}
虽然这里每次循环都 defer 可能略显冗余,但在复杂逻辑或包含分支、return 的函数中,这种模式能有效避免资源泄漏。
结构体中嵌入 Mutex 的常见模式
实际开发中,Mutex 通常作为结构体字段,用来保护结构体内部状态:
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
这样封装后,所有对外暴露的方法都自动具备线程安全性,调用者无需关心同步细节。
基本上就这些。只要记住:共享资源 + 并发写 = 必须加锁。合理使用 sync.Mutex 能有效防止数据竞争,提升程序稳定性。










