为什么 Go 在这段代码中的 Printf 上检测到竞争条件

我编写了一些简单的 Go 代码来理解竞争条件,如下所示:


package main


import (

    "fmt"

    "sync"

)


type outer struct {

    sync.Mutex

    num int

    foo string

}


func (outer *outer) modify(wg *sync.WaitGroup) {

    outer.Lock()

    defer outer.Unlock()

    outer.num = outer.num + 1

    wg.Done()

}


func main() {

    outer := outer{

        num: 2,

        foo: "hi",

    }

    var w sync.WaitGroup

    for j := 0; j < 5000; j++ {

        w.Add(1)

        go outer.modify(&w)

    }

    w.Wait()

    fmt.Printf("Final is %+v", outer)


}


当我在上面运行时,打印的答案始终是正确的,即 num 始终是 5002。如果没有锁,由于 forloop 中创建的 goroutine 之间的竞争,答案将无法预测。

但是,当我使用 -race 运行此命令时,会检测到以下竞争条件:



go run -race random.go

==================

WARNING: DATA RACE

Read at 0x00c00000c060 by main goroutine:

  main.main()

      random.go:32 +0x15d


Previous write at 0x00c00000c060 by goroutine 22:

  sync/atomic.AddInt32()

      /usr/local/go/src/runtime/race_amd64.s:269 +0xb

  sync.(*Mutex).Unlock()

      /usr/local/go/src/sync/mutex.go:182 +0x54

  main.(*outer).modify()

      random.go:19 +0xb7


Goroutine 22 (finished) created at:

  main.main()

      random.go:29 +0x126

==================

Final is {Mutex:{state:0 sema:0} num:5002 foo:hi}Found 1 data race(s)

exit status 66

IE。它正在检测最终的 Printf 和在其之前创建的一个随机 go 例程之间的竞争。由于我使用等待来同步,所以当我们到达 Printf 时,所有 go 例程都已完成。


比赛被报道的原因是什么?


我还需要锁定打印结构吗?


慕仙森
浏览 76回答 2
2回答

holdtom

使用不当sync.WaitGroup是导致您出现竞争状况的原因。其中任何一个都应该正常工作:func (outer *outer) modify(wg *sync.WaitGroup) {&nbsp; &nbsp; outer.Lock()&nbsp; &nbsp; outer.num = outer.num + 1&nbsp; &nbsp; outer.Unlock()&nbsp; &nbsp; wg.Done()}func (outer *outer) modify(wg *sync.WaitGroup) {&nbsp; &nbsp; outer.Lock()&nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; defer outer.Unlock()&nbsp; &nbsp; outer.num = outer.num + 1}wg.Done()应该在解锁互斥锁之后调用(延迟调用以 LIFO 方式进行),因为之前调用它会导致调用Printf()与最后一个outer.Unlock()调用竞争以访问outer.

万千封印

package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "sync")type outer struct {&nbsp; &nbsp; *sync.Mutex&nbsp; &nbsp; num int&nbsp; &nbsp; foo string}func (outer *outer) modify(wg *sync.WaitGroup) {&nbsp; &nbsp; outer.Lock()&nbsp; &nbsp; defer outer.Unlock()&nbsp; &nbsp; outer.num++&nbsp; &nbsp; wg.Done()}func main() {&nbsp; &nbsp; outer := outer{&nbsp; &nbsp; &nbsp; &nbsp; Mutex: &sync.Mutex{},&nbsp; &nbsp; &nbsp; &nbsp; num:&nbsp; &nbsp;2,&nbsp; &nbsp; &nbsp; &nbsp; foo:&nbsp; &nbsp;"hi",&nbsp; &nbsp; }&nbsp; &nbsp; w := &sync.WaitGroup{}&nbsp; &nbsp; for j := 0; j < 5000; j++ {&nbsp; &nbsp; &nbsp; &nbsp; w.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go outer.modify(w)&nbsp; &nbsp; }&nbsp; &nbsp; w.Wait()&nbsp; &nbsp; fmt.Printf("Final is %+v", outer)}将sync.Mutex 更改为指针。我认为这是由于sync.Mutex 在您的版本中很有价值
打开App,查看更多内容
随时随地看视频慕课网APP