You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言Mutex未锁定问题求助:非指针接收者函数可访问受保护结构体

sync.Mutex未阻止无锁操作的原因

我在使用sync.Mutex时发现了异常行为,因此编写了如下测试代码:

package main

import (
    "fmt"
    "sync"
)

type Box struct {
    Stuff int
    mu    sync.Mutex
}

var box Box

func init() {
    box = Box{}
    box.mu.Lock()
}

func (box *Box) add() {
    fmt.Println("add()")
    box.mu.Unlock()
    box.Stuff++
    box.mu.Lock()
}

func (box *Box) display() {
    fmt.Print("display(): ")
    box.mu.Unlock()
    fmt.Println(box.Stuff)
    box.mu.Lock()
}

func add2() {
    fmt.Println("add2()")
    box.Stuff++ //increments but should not, error expected
}

func display2() {
    fmt.Print("display2(): ")
    fmt.Println(box.Stuff) //displays but should not, error expected
}

func main() {
    box.display()
    box.add()
    box.display()
    display2()
    add2()
    box.display()
}

运行代码后得到如下输出:

display(): 0
add()
display(): 1
display2(): 1
add2()
display(): 2

我原本预期名称后缀为2的两个函数(add2()和display2())会执行失败,但实际并未出现报错。请问这是什么原因?


问题解析

1. sync.Mutex的本质特性

Go的sync.Mutex是被动的互斥工具,它不会主动检测或阻止对共享资源的无锁访问:

  • 只有当调用Lock()时发现锁已被持有,才会阻塞当前goroutine;
  • 只有当调用Unlock()的goroutine并未持有该锁时,才会触发运行时panic;
  • 直接访问共享字段(比如box.Stuff)的操作,完全不受Mutex约束,自然不会产生任何报错。

2. 你的代码逻辑漏洞

你在init()里调用了box.mu.Lock(),但后续的add()和display()都是先执行Unlock(),操作Stuff,再重新Lock()——这意味着在Unlock()到Lock()的这段时间里,锁处于完全释放的状态。add2()和display2()的操作刚好发生在这些解锁窗口中,所以它们能顺利访问Stuff字段,不会被阻塞或报错。

3. 正确的Mutex使用方式

要让Mutex真正保护共享资源,所有访问/修改共享字段的操作都必须在持有锁的状态下进行:

  • 进入函数后先调用Lock(),操作完成后调用Unlock();
  • 推荐使用defer box.mu.Unlock()确保锁被正确释放,避免因代码分支遗漏解锁;
  • 不要在持有锁的情况下执行IO、sleep等耗时操作,防止阻塞其他goroutine。

修正后的示例代码:

package main

import (
    "fmt"
    "sync"
)

type Box struct {
    Stuff int
    mu    sync.Mutex
}

var box Box

func (box *Box) add() {
    fmt.Println("add()")
    box.mu.Lock()
    defer box.mu.Unlock()
    box.Stuff++
}

func (box *Box) display() {
    fmt.Print("display(): ")
    box.mu.Lock()
    defer box.mu.Unlock()
    fmt.Println(box.Stuff)
}

func add2() {
    fmt.Println("add2()")
    box.mu.Lock()
    defer box.mu.Unlock()
    box.Stuff++ // 现在必须持有锁才能操作
}

func display2() {
    fmt.Print("display2(): ")
    box.mu.Lock()
    defer box.mu.Unlock()
    fmt.Println(box.Stuff) // 现在必须持有锁才能读取
}

func main() {
    box.display()
    box.add()
    box.display()
    display2()
    add2()
    box.display()
}

内容的提问来源于stack exchange,提问作者pigfox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:19:47