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

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

Why does this WaitGroup sometimes not wait for all goroutines?

问题

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

  1. type Scratch struct {
  2. //sync.RWMutex
  3. Itch []int
  4. }
  5. func (s *Scratch) GoScratch(done chan bool, j int) error {
  6. var ws sync.WaitGroup
  7. if len(s.Itch) == 0 {
  8. s.Rash = make([]int, 0)
  9. }
  10. for i := 0; i < j; i++ {
  11. ws.Add(1)
  12. go func (i int) {
  13. defer ws.Done()
  14. s.Rash = append(s.Rash, i)
  15. }(i)
  16. }
  17. ws.Wait()
  18. done <- true
  19. return nil
  20. }
  21. func main() {
  22. done := make(chan bool, 3)
  23. s := &Scratch{}
  24. err := s.GoScratch(done, 3)
  25. if err != nil {
  26. log.Println("Error: ", err)
  27. }
  28. <-done
  29. log.Println("Length: ", len(s.Rash))
  30. }

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

英文:

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

  1. type Scratch struct {
  2. //sync.RWMutex
  3. Itch []int
  4. }
  5. func (s *Scratch) GoScratch(done chan bool, j int) error {
  6. var ws sync.WaitGroup
  7. if len(s.Itch) == 0 {
  8. s.Rash = make([]int, 0)
  9. }
  10. for i := 0; i &lt; j; i++ {
  11. ws.Add(1)
  12. go func (i int) {
  13. defer ws.Done()
  14. s.Rash = append(s.Rash, i)
  15. }(i)
  16. }
  17. ws.Wait()
  18. done&lt;- true
  19. return nil
  20. }
  21. func main() {
  22. done := make(chan bool, 3)
  23. s := &amp;Scratch{}
  24. err := s.GoScratch(done, 3)
  25. if err != nil {
  26. log.Println(&quot;Error:%v&quot;,err)
  27. }
  28. &lt;-done
  29. log.Println(&quot;Length: &quot;, len(s.Rash))
  30. }`

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

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

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

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

  1. // 创建一个全局互斥锁
  2. var mutex = &sync.Mutex{}
  3. // 使用互斥锁来防止竞态条件
  4. go func (i int) {
  5. defer ws.Done()
  6. defer mutex.Unlock() // 确保互斥锁解锁
  7. // 在访问资源之前锁定它
  8. mutex.Lock()
  9. s.Rash = append(s.Rash, i)
  10. }(i)

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

英文:

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

  1. go func (i int) {
  2. defer ws.Done()
  3. // race condition on s.Rash access
  4. s.Rash = append(s.Rash, i)
  5. }(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:

  1. // create a global mutex
  2. var mutex = &amp;sync.Mutex{}
  3. // use mutex to prevent race condition
  4. go func (i int) {
  5. defer ws.Done()
  6. defer mutex.Unlock() // ensure that mutex unlocks
  7. // Lock the resource before accessing it
  8. mutex.Lock()
  9. s.Rash = append(s.Rash, i)
  10. }(i)

You can read more about this here and here.

答案2

得分: 6

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

  1. go test -race .

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

英文:

If you run your code with race detector

  1. 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:

确定