在golang中,如果两个goroutine读写没有互斥体和原子的变量,则可能会导致数据竞争。
使用命令go run --race xxx.go
将检测比赛点。
在src / sync / mutex.go中实现Mutex时,请使用以下代码
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
var waitStartTime int64
starving := false
awoke := false
iter := 0
old := m.state // This line confuse me !!!
......
代码old := m.state
使我感到困惑,因为m.state是由不同的goroutine读写的。
以下功能测试明显有竞态条件问题。但是,如果我将其放在Mutex.go中,则不会有种族状况检测到。
# mutex.go
func Test(){
a := int32(1)
go func(){
atomic.CompareAndSwapInt32(&a, 1, 4)
}()
_ = a
}
如果将其放在src / os / exec.go之类的其他软件包中,则将检测到条件竞争问题。
package main
import(
"sync"
"os"
)
func main(){
sync.Test() // race condition will not detect
os.Test() // race condition will detect
}
答案 0 :(得分:2)
首先,golang源始终会发生变化,因此,请确保我们正在寻找相同的东西。在
处获取版本1.12https://github.com/golang/go/blob/release-branch.go1.12/src/sync/mutex.go
如您所说,锁定功能开始
func (m *Mutex) Lock() {
// fast path where it will set the high order bit and return if not locked
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
return
}
//reads value to decide on the lower order bits
for {
//if statements involving CompareAndSwaps on the lower order bits
}
}
CompareAndSwap在做什么?它在int32中看起来很原子,如果为0,则将其交换为MutexLocked(上面定义为const的1)并返回true,表示交换了它。 然后它立即返回。那是它的捷径。 goroutine获得了锁,现在它正在运行,可以开始运行它的受保护路径。
如果已经为1(mutexLocked),则不交换它,并返回false(不交换)。
然后,它读取状态并进入一个循环,进行原子比较并交换以确定其行为。
可能的状态是什么?从const块中看到的锁定,唤醒和饥饿的组合。
现在,根据goroutine在等待列表上等待了多长时间,它将优先考虑何时再次检查互斥体是否空闲。
但还要注意,只有Unlock()才能将MutexLocked位设置回0。 在Lock()CAS循环中,唯一设置的位是挨饿和唤醒的位。是的,您可以有多个读取器,但随时只有一个写入器,并且该写入器是持有互斥锁并正在执行其保护的那个位直到调用Unlock()为止的路径。查看this article了解更多详细信息。
答案 1 :(得分:0)
通过反汇编二进制输出文件,不同包中的Test函数生成不同的代码。
原因是编译器禁止在同步包中生成竞争检测工具。
代码是:
var norace_inst_pkgs = []string{"sync", "sync/atomic"} // https://github.com/golang/go/blob/release-branch.go1.12/src/cmd/compile/internal/gc/racewalk.go
``