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

时间:2019-04-29 12:48:04

标签: go conditional-statements mutex

在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
}

2 个答案:

答案 0 :(得分: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(上面定义为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
``