去RWMutex仍然会引发竞争状况?

ste*_*iel 3 concurrency mutex go race-condition

我有一个看似无辜的包,它只是制作切片并用RWMutex保护它.然而,当我运行它时,它仍然抱怨竞争条件.我究竟做错了什么?(游乐场)

type Ids struct {
    e []int64
    sync.RWMutex
}

func (i *Ids) Read() []int64 {
    i.RLock()
    defer i.RUnlock()

    return i.e
}


func (i *Ids) Append(int int64) {
    i.Lock()
    defer i.Unlock()

    i.e = append(i.e, int)
}

func main() {
    t := &Ids{e: make([]int64, 1)}

    for i := 0; i < 100; i++ {
        go func() {
            fmt.Printf("%v\n", t.Read())
        }()

        go func() {
            t.Append(int64(i))
        }()
    }

    time.Sleep(time.Second * 10)
}
Run Code Online (Sandbox Code Playgroud)

当运行时-race,它返回(除其他外):

==================
WARNING: DATA RACE
Read at 0x00c4200a0010 by goroutine 7:
  main.main.func2()
      .../main.go:38 +0x38

Previous write at 0x00c4200a0010 by main goroutine:
  main.main()
      .../main.go:32 +0x197

Goroutine 7 (running) created at:
  main.main()
      .../main.go:37 +0x173
==================
Run Code Online (Sandbox Code Playgroud)

Ste*_*erg 6

您正在i多个goroutine 中捕获相同的变量.

解决此问题的一种方法是修改main for循环,如下所示:

for i := 0; i < 100; i++ {
    i := i  # look here
    go func() {
        fmt.Printf("%v\n", t.Read())
    }()

    go func() {
        t.Append(int64(i))
    }()
}
Run Code Online (Sandbox Code Playgroud)

这将确保您在for循环的每次迭代中捕获第二个goroutine闭包中的不同变量.在您的示例中,i传递给的t.Append是与ifor循环同时增加的相同.

我还建议您go vet在将来运行以捕获此类错误.有关更多信息,请参阅Go常见问题解答中的详细信息:https://golang.org/doc/faq#closures_and_goroutines