【问题标题】:Goroutine with sync.waitGroup output different value each time带有sync.waitGroup的Goroutine每次输出不同的值
【发布时间】:2020-08-20 03:50:48
【问题描述】:

下面的代码每次执行后都会打印不同的值,但我希望值相同,如何在不使用time.Sleep的情况下更改下面的代码

package main

import (
    "fmt"
    "sync"
)

var total int
var wg sync.WaitGroup

// Inc increments the counter for the given key.
func inc(num int) {
    total += num
    wg.Done()
}

// Value returns the current value of the counter for the given key.
func getValue() int {
    return total
}

func main() {
    for i := 1; i <= 1000; i++ {
        wg.Add(1)
        go inc(i)
    }
    wg.Wait()
    fmt.Println(getValue())
}

【问题讨论】:

    标签: go goroutine


    【解决方案1】:

    你有一个数据竞赛,结果是不确定的。您必须同步对共享变量的访问:

    var total int
    var lock sync.Mutex
    var wg sync.WaitGroup
    
    // Inc increments the counter for the given key.
    func inc(num int) {
        lock.Lock()
        defer lock.Unlock()
        total += num
        wg.Done()
    }
    
    // Value returns the current value of the counter for the given key.
    func getValue() int {
        lock.Lock()
        defer lock.Unlock()
        return total
    }
    

    或者,使用sync/atomic 访问/修改变量。

    【讨论】:

    • 你好,getValue里面有必要使用sync.lock吗?因为它不在 goroutine 内部,它在 wg.Wait() 函数之后被调用
    • 它在一个 goroutine 中,运行 main()。但是,wg.Wait 确实建立了发生前的关系,因此只要 getValue 调用是该调用的唯一实例,它就应该在没有锁的情况下工作。不过,我想知道比赛检测器是否将其视为一场比赛。
    【解决方案2】:

    你每次得到不同值的原因是total += num中的竞争条件。

    一个简单的解决方法是添加一个互斥锁: var mu sync.Mutex

    并在inc 中使用它:

    func inc(num int) {
        mu.Lock()
        defer mu.Unlock()
        total += num
        wg.Done()
    }
    

    【讨论】:

      【解决方案3】:

      已经提到您有“数据竞争”,使用Mutex 是一种解决方案。或者你可以使用atomic 包,这样更快。

      package main
      
      import (
          "fmt"
          "sync"
          "sync/atomic"
      )
      
      var total uint64
      var wg sync.WaitGroup
      
      func inc(num uint64) {
          atomic.AddUint64(&total, 1)
          wg.Done()
      }
      
      // Value returns the current value of the counter for the given key.
      func getValue() uint64 {
          return atomic.LoadUint64(&total)
      }
      
      func main() {
          for i := uint64(1); i <= 1000; i++ {
              wg.Add(1)
              go inc(i)
          }
          wg.Wait()
          fmt.Println(getValue())
      }
      

      【讨论】:

      • 您好,是 AddUint64 出于某种原因,还是 AddUint32 也可以使用? atomic 和 mutex 之间的性能如何。
      • @eason,如果uint32 符合您的需要,请使用AddUint32()。性能取决于许多因素。在我的 PC 工作台上,play.golang.org/p/Do9mTKX4ExV 显示 6 vs 16 ns/op
      • 我的操作系统是32位还是64位?
      • @eason 在 win32 上使用uint64 可以,反之亦然
      猜你喜欢
      • 2017-08-27
      • 1970-01-01
      • 2020-12-25
      • 1970-01-01
      • 2018-10-31
      • 2015-05-11
      • 2017-06-14
      • 2018-08-02
      相关资源
      最近更新 更多