sync:在之前的Wait返回之前重用WaitGroup

问题描述 投票:0回答:1

该代码在多个goroutine中并发运行,以下代码是从生产环境代码中提取的关键相关部分:

func check() {

    .......check condition......

    //skipEnsure and skipNative will not both false here
    var wg sync.WaitGroup
    if !skipEnsure {
        wg.Add(1)
        go func(wg *sync.WaitGroup) {
            defer wg.Done()
            dosomething1()
        }(&wg)
    }
    if !skipNative {
        wg.Add(1)
        go func(wg *sync.WaitGroup) {
            defer wg.Done()
            dosomething2()
        }(&wg)
    }
    wg.Wait() //panic here
}

func worker() {
    ......

    go check()
}

func main() {
    for i:=0;i<32;i++ {
        go worker()
    }
    ch := make(chan any, 0)
    <-ch
}

当 WaitGroup 是全局变量时,我可以重现该问题,但不能在提供的代码中重现该问题。在生产环境运行一段时间后会出现panic。

go mutex goroutine waitgroup
1个回答
0
投票

如果我理解正确的话,代码看起来像这样:

 1  package main
 2  
 3  import (
 4      "sync"
 5      "time"
 6  )
 7  
 8  var wg sync.WaitGroup
 9  
10  func work() {
11      wg.Add(1)
12      go func(wg *sync.WaitGroup) {
13          defer wg.Done()
14          time.Sleep(1 * time.Millisecond)
15          println("one done")
16      }(&wg)
17      wg.Add(1)
18      go func(wg *sync.WaitGroup) {
19          defer wg.Done()
20          time.Sleep(2 * time.Millisecond)
21          println("two done")
22      }(&wg)
23      wg.Wait() //panic here
24  }
25  
26  func main() {
27      for i := 0; i < 10; i++ {
28          go work()
29      }
30      wg.Wait()
31      println("done")
32  }

sync.WaitGroup 不是为这种用途而设计的。何时调用 wg.Add 和 wg.Wait 并不明显。如果您尝试使用数据竞争检测器运行它,您可以看到类似这样的内容:

==================
WARNING: DATA RACE
Write at 0x0000011707e8 by goroutine 6:
  runtime.racewrite()
      <autogenerated>:1 +0x24
  main.work()
      /1.go:23 +0x1b2

Previous read at 0x0000011707e8 by goroutine 7:
  runtime.raceread()
      <autogenerated>:1 +0x24
  main.work()
      /1.go:11 +0x35

Goroutine 6 (running) created at:
  main.main()
      /1.go:28 +0x32

Goroutine 7 (running) created at:
  main.main()
      /1.go:28 +0x32
==================
Found 1 data race(s)

您可以为每个 goroutine 使用自己的 WaitGroup (如您的示例所示)。我无法想象有什么理由反对它。例如,这段代码可以正常工作,没有任何数据竞争:

package main

import (
    "sync"
    "time"
)

func work() {
    var wg sync.WaitGroup
    wg.Add(1)
    go func(wg *sync.WaitGroup) {
        defer wg.Done()
        time.Sleep(1 * time.Millisecond)
        println("one done")
    }(&wg)
    wg.Add(1)
    go func(wg *sync.WaitGroup) {
        defer wg.Done()
        time.Sleep(2 * time.Millisecond)
        println("two done")
    }(&wg)
    wg.Wait() //panic here
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            work()
            wg.Done()
        }()
    }
    wg.Wait()
    println("done")
}
© www.soinside.com 2019 - 2024. All rights reserved.