douwu8524 2018-12-15 08:06
浏览 12
已采纳

使用WaitGroup测试竞赛条件时出现意外行为

I had a task to simulate race conditions in Go. However, I've run into a case, that I am unable to explain. The code snippet below

package main

import (
    "fmt"
    "sync"
)

var value, totalOps, totalIncOps, totalDecOps int

func main() {
    fmt.Println("Total value: ", simulateRacing(10000))
    fmt.Print("Total iterations: ", totalOps)
    fmt.Print(" of it, increments: ", totalIncOps)
    fmt.Print(", decrements: ", totalDecOps)
}

// Function to simulate racing condition
func simulateRacing(iterationsNumber int) int {
    value = 0
    // Define WaitGroup
    var waitGroup sync.WaitGroup
    waitGroup.Add(2)

    go increaseByOne(iterationsNumber, &waitGroup)
    go decreaseByOne(iterationsNumber, &waitGroup)

    waitGroup.Wait()

    return value
}

// Function to do N iterations, each time increasing value by 1
func increaseByOne(N int, waitGroup *sync.WaitGroup) {
    for i := 0; i < N; i++ {
        value++
        // Collecting stats
        totalOps++
        totalIncOps++
    }
    waitGroup.Done()
}

// Same with decrease
func decreaseByOne(N int, waitGroup *sync.WaitGroup) {
    for i := 0; i < N; i++ {
        value--
        // Collecting stats
        totalOps++
        totalDecOps++
    }
    waitGroup.Done()
}

In my understanding, it should produce consistent (deterministic) result each time, since we are doing the same number of increments and decrements, with a WaitGroup making sure both functions will execute.

However, each time output is different, with only increments and decrements counters staying the same. Total value: 2113 Total iterations: 17738 of it, increments: 10000, decrements: 10000 and Total value: 35 Total iterations: 10741 of it, increments: 10000, decrements: 10000

Maybe you can help me to explain this behaviour? Why total iterations counter and value itself is non-deterministic?

  • 写回答

2条回答 默认 最新

  • dsy48837 2018-12-15 08:28
    关注

    That's a classical example of race condition. value++ is not an atomic operation, so there are no guarantees that it will work correctly or deterministically when called from multiple threads without synchronization.

    To give some intuition, value++ is more or less equivalent to value = value + 1. You can think of it as three operations, not one: load value from memory to a CPU register, increase value in the register (you cannot modify memory directly), store the value back to the memory. Two threads may load the same value simultaneously, increase it, get the same result, and then write it back, so it effectively increases value by 1, not two.

    As order of operations between threads is non-deterministic, result is also non-deterministic.

    The same effect happens with totalOps. However, totalIncOps and totalDecOps are only ever modified/read by a single thread, so there is no race here and their end values are deterministic.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题
  • ¥15 matlab中使用gurobi时报错
  • ¥15 这个主板怎么能扩出一两个sata口
  • ¥15 不是,这到底错哪儿了😭
  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么