-3

考虑下面的代码,在我看来,val 将在 100 到 200 之间,但它总是 200

var val = 0

func main() {
    num := runtime.NumCPU()
    fmt.Println("使用cpu数量", num)
    go add("A")
    go add("B")
    time.Sleep(1 * time.Second)
    fmt.Println("val的最终结果", val)
}

func add(proc string) {
    for i := 0; i < 100; i++ {
        val++
        fmt.Printf("execute process[%s] and val is %d\n", proc, val)
        time.Sleep(5 * time.Millisecond)
    }
}

为什么 val 最后总是 200?

4

2 回答 2

1

您的代码有两个问题:

  1. 你有一个数据竞争——val在没有同步的情况下同时写入和读取。它的存在使得对程序结果的推理变得毫无意义。
  2. 1 秒的睡眠main()时间太短 - 1 秒后 goroutines 可能还没有完成。您根本不希望fmt.Printf花费任何时间,但控制台输出确实需要大量时间(在某些操作系统上比其他操作系统长)。所以循环不会花费 100 * 5 = 500 毫秒,而是要长得多。

这是一个固定版本,它以原子方式递增val并正确等待两个 goroutine 完成,而不是假设它们将在 1 秒内完成。

var val = int32(0)

func main() {
    num := runtime.NumCPU()
    fmt.Println("使用cpu数量", num)
    var wg sync.WaitGroup
    wg.Add(2)
    go add("A", &wg)
    go add("B", &wg)
    wg.Wait()
    fmt.Println("val的最终结果&quot;, atomic.LoadInt32(&val))
}

func add(proc string, wg *sync.WaitGroup) {
    for i := 0; i < 100; i++ {
        tmp := atomic.AddInt32(&val, 1)
        fmt.Printf("execute process[%s] and val is %d\n", proc, tmp)
        time.Sleep(5 * time.Millisecond)
    }
    wg.Done()
}
于 2021-09-14T18:07:22.507 回答
0

递增整数只需要几纳秒,而每个 goroutine 在每次递增之间等待 5 毫秒。

这意味着,每个 goroutine 仅花费大约 1,000,000 分之一的时间实际执行操作,其余时间都在休眠。因此,发生干扰的可能性非常低(因为两个 goroutine 需要同时进行操作)。

即使两个 goroutine 在相同的计时器上,库的实际精度time也远不及持续产生冲突所需的纳秒级。另外,goroutines 正在做一些打印,这将进一步分散时间。


正如评论中所指出的,您的代码确实仍然存在数据竞争(您的意图似乎是这样),这意味着尽管有任何观察结果,但我们无法确定您的程序的输出。它可能会输出 100-200 之间的任何数字,或完全不同的数字。

于 2021-09-14T17:43:59.600 回答