1

我正在尝试了解 golang频道同步当我使用竞赛检测器运行我的程序时,它会导致竞赛检测。

我的程序:

func main() {
    ch := make(chan int)
    done := make(chan struct{})
    wg := sync.WaitGroup{}

    go func() {
        defer close(ch)
        defer close(done)
        wg.Wait()
        done <- struct{}{}
    }()

    for i := 0; i < 5; i++ {
        x := i
        wg.Add(1)
        go func() {
            defer wg.Done()
            fmt.Println("Value: ", x)
            ch <- x
        }()
    }
    
loop:
    for {
        select {
        case i := <-ch:
            fmt.Println("Value: ", i)
        case <- done:
            break loop
        }
    }
}

种族检测器报告:

==================
WARNING: DATA RACE
Write at 0x00c000020148 by goroutine 7:
  internal/race.Write()
      /home/linuxbrew/.linuxbrew/Cellar/go/1.16.5/libexec/src/internal/race/race.go:41 +0x125
  sync.(*WaitGroup).Wait()
      /home/linuxbrew/.linuxbrew/Cellar/go/1.16.5/libexec/src/sync/waitgroup.go:128 +0x126
  main.main.func1()
      /home/reddy/code/github.com/awesomeProject/prod.go:106 +0xc4

Previous read at 0x00c000020148 by main goroutine:
  internal/race.Read()
      /home/linuxbrew/.linuxbrew/Cellar/go/1.16.5/libexec/src/internal/race/race.go:37 +0x206
  sync.(*WaitGroup).Add()
      /home/linuxbrew/.linuxbrew/Cellar/go/1.16.5/libexec/src/sync/waitgroup.go:71 +0x219
  main.main()
      /home/reddy/code/github.com/awesomeProject/prod.go:112 +0x124

Goroutine 7 (running) created at:
  main.main()
      /home/reddy/code/github.com/awesomeProject/prod.go:103 +0x104
==================

我无法弄清楚这里出了什么问题。

我的分析:

  1. wg.Add(1)正在递增计数器
  2. wg.Done()在减少计数器的 goroutine 结束时调用
  3. ch <- x这应该是一个阻塞调用,因为它是非缓冲通道
  4. 循环应该迭代直到完成通道有一些消息发生在waitgroup计数器变为零时,即所有 5 个 goroutine 都发布了消息
  5. 一旦计数器变为零,wggoroutine 将恢复并调用 done,一旦消息在主循环中被消耗,它会中断循环并应该优雅地退出。
4

1 回答 1

6

该程序在对 的调用wg.Add和对 的调用之间存在竞争wg.Wait。这些调用可以以任何顺序发生。在调用 to之前调用 towg.Wait不会等待任何 goroutines 。wg.Waitwg.Add

通过wg.Add 启动调用wg.Wait. 此更改确保调用wg.Add发生在调用wg.Wait.

for i := 0; i < 5; i++ {
    x := i
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println("Value: ", x)
        ch <- x
    }()
}

go func() {
    defer close(ch)
    defer close(done)
    wg.Wait()
    done <- struct{}{}
}()

当在竞争检测器下运行时,该WaitGroup类型有代码来检查此错误(建模读取建模写入)。

ch通过在关闭时中断主 goroutine 中的循环来简化代码。done不需要频道。

ch := make(chan int)
wg := sync.WaitGroup{}

for i := 0; i < 5; i++ {
    x := i
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println("Value: ", x)
        ch <- x
    }()
}

go func() {
    wg.Wait()
    close(ch)
}()

for i := range ch {
    fmt.Println("Value: ", i)
}
于 2021-06-18T04:51:13.723 回答