在互斥体的golang实现中是否存在竞争条件,m.state在没有原子函数的情况下被读取

问题描述 投票:0回答:2

在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
}
go condition mutex
2个回答
2
投票

首先,golang源总是会改变,所以让我们确保我们看到同样的事情。以1.12发布

https://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(1定义为上面的const)并返回true以交换它。然后它立即返回。这是它快速的道路。 goroutine获得了锁定,现在它正在运行,可以开始运行它的受保护路径。

如果它已经是1(mutexLocked),它不会交换它并返回false(它没有交换它)。

然后它读取状态并进入一个循环,它进行原子比较和交换以确定它应该如何表现。

可能的状态是什么?从const块看到的锁定,吵醒和挨饿的组合。

现在,根据goroutine等待等待列表的时间长短,它将优先考虑何时再次检查互斥锁是否空闲。

但是也观察到只有Unlock()可以将mutexLocked位设置回0.在Lock()CAS循环中,设置的唯一位是饥饿和唤醒。你可以有多个读者,但任何时候只有一个写入者并且该编写者是持有互斥锁并且在调用Unlock()之前执行其受保护路径的那个。查看this article了解更多详情。


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
``

© www.soinside.com 2019 - 2024. All rights reserved.