为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕仙森 2023-07-31 16:43:24
我编写了一些简单的 Go 代码来理解竞争条件,如下所示:package mainimport (    "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 RACERead at 0x00c00000c060 by main goroutine:  main.main()      random.go:32 +0x15dPrevious 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 +0xb7Goroutine 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 66IE。它正在检测最终的 Printf 和在其之前创建的一个随机 go 例程之间的竞争。由于我使用等待来同步,所以当我们到达 Printf 时,所有 go 例程都已完成。比赛被报道的原因是什么?我还需要锁定打印结构吗?
查看完整描述

2 回答

?
holdtom

TA贡献1805条经验 获得超10个赞

使用不当sync.WaitGroup是导致您出现竞争状况的原因。其中任何一个都应该正常工作:


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

    outer.Lock()

    outer.num = outer.num + 1

    outer.Unlock()

    wg.Done()

}

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

    outer.Lock()

    defer wg.Done()

    defer outer.Unlock()

    outer.num = outer.num + 1

}

wg.Done()应该在解锁互斥锁之后调用(延迟调用以 LIFO 方式进行),因为之前调用它会导致调用Printf()与最后一个outer.Unlock()调用竞争以访问outer.


查看完整回答
反对 回复 2023-07-31
?
万千封印

TA贡献1891条经验 获得超3个赞

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

    wg.Done()

}


func main() {

    outer := outer{

        Mutex: &sync.Mutex{},

        num:   2,

        foo:   "hi",

    }

    w := &sync.WaitGroup{}

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

        w.Add(1)

        go outer.modify(w)

    }

    w.Wait()

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

}

将sync.Mutex 更改为指针。


我认为这是由于sync.Mutex 在您的版本中很有价值


查看完整回答
反对 回复 2023-07-31
  • 2 回答
  • 0 关注
  • 75 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信