为什么这个 WaitGroup 有时候不能等待所有的 goroutine?

huangapple go评论166阅读模式
英文:

Why does this WaitGroup sometimes not wait for all goroutines?

问题

下面的代码有时会输出2。为什么等待组(wait group)没有等待所有的goroutine完成?

type Scratch struct {
    //sync.RWMutex
    Itch []int
}

func (s *Scratch) GoScratch(done chan bool, j int) error {

    var ws sync.WaitGroup

    if len(s.Itch) == 0 {
        s.Rash = make([]int, 0)
    }
    for i := 0; i < j; i++ {
        ws.Add(1)
        go func (i int) {
            defer ws.Done()
            s.Rash = append(s.Rash, i) 
        }(i)
    }
    ws.Wait()
    done <- true
    return nil
}

func main() {
    done := make(chan bool, 3)
    s := &Scratch{}
    err := s.GoScratch(done, 3)
    if err != nil {
        log.Println("Error: ", err)
    }
    <-done
    log.Println("Length: ", len(s.Rash)) 
}

奇怪的是,我无法通过main函数输出2,但是当我使用一个测试用例时,有时会输出2。

英文:

The code below outputs 2 sometimes. Why isn't the wait group waiting for all the goroutines to complete ?

type Scratch struct {
    //sync.RWMutex
    Itch []int
}

func (s *Scratch) GoScratch(done chan bool, j int) error {

    var ws sync.WaitGroup

    if len(s.Itch) == 0 {
            s.Rash = make([]int, 0)
    }
    for i := 0; i &lt; j; i++ {
            ws.Add(1)
            go func (i int) {
                    defer ws.Done()
          
                   s.Rash = append(s.Rash, i) 
            }(i)
    }
    ws.Wait()
    done&lt;- true
    return nil
}

func main() {
    done := make(chan bool, 3)
    s := &amp;Scratch{}
    err := s.GoScratch(done, 3)
    if err != nil {
            log.Println(&quot;Error:%v&quot;,err)
    }
    &lt;-done
    log.Println(&quot;Length: &quot;, len(s.Rash)) 
}`

Strangely i can't get it to output 2 with a main function but when I use a test case it outputs 2 sometimes.

答案1

得分: 10

你的代码中存在竞态条件。它就在这里:

go func (i int) {
    defer ws.Done()
    // 在 s.Rash 访问上存在竞态条件
    s.Rash = append(s.Rash, i) 
}(i)

由于所有的 goroutine 都同时访问 s.Rash,这可能导致切片的更新被覆盖。尝试使用 sync.Mutex 运行相同的代码来防止这种情况发生:

// 创建一个全局互斥锁
var mutex = &sync.Mutex{}

// 使用互斥锁来防止竞态条件
go func (i int) {
    defer ws.Done()
    defer mutex.Unlock() // 确保互斥锁解锁
    
    // 在访问资源之前锁定它
    mutex.Lock()
    s.Rash = append(s.Rash, i) 
}(i)

你可以在这里这里了解更多信息。

英文:

There is a race condition in your code. It is right here:

go func (i int) {
    defer ws.Done()
    // race condition on s.Rash access
    s.Rash = append(s.Rash, i) 
}(i)

Since all the goroutines access s.Rash concurrently, this may cause the slice updates to be overwritten. Try running the same code with sync.Mutex locking to prevent this:

// create a global mutex
var mutex = &amp;sync.Mutex{}

// use mutex to prevent race condition
go func (i int) {
    defer ws.Done()
    defer mutex.Unlock() // ensure that mutex unlocks
    
    // Lock the resource before accessing it
    mutex.Lock()
    s.Rash = append(s.Rash, i) 
}(i)

You can read more about this here and here.

答案2

得分: 6

如果你使用竞争检测器运行你的代码:

go test -race .

你会发现在切片s.Rash上存在竞争条件。

英文:

If you run your code with race detector

go test -race .

you will find out race condition on slice s.Rash.

huangapple
  • 本文由 发表于 2016年8月17日 00:59:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/38980565.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定