-3

consider below code, in my opinon, val will between 100 and 200, but it's always 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)
    }
}

why val always is 200 at last?

space
  • 27
  • 4

2 Answers2

1

There are 2 problems with your code:

  1. You have a data race - concurrently writing and reading val without synchronization. The presence of it makes reasoning about program outcome meaningless.
  2. The sleep in main() of 1 second is too short - the goroutines may not be done yet after 1 second. You expect fmt.Printf to take no time at all, but console output does take significant amount of time (on some OS longer than others). So the loop won't take 100 * 5 = 500 milliseconds, but much, much longer.

Here's a fixed version that atomically increments val and properly waits for both goroutines to finish instead of assuming that they will be done within 1 second.

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的最终结果", 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()
}
rustyx
  • 80,671
  • 25
  • 200
  • 267
0

Incrementing the integer will take only on the order of a few nanoseconds, while each goroutine is waiting 5 milliseconds between each increment.

Meaning, each goroutine is only spending about a 1,000,000th of the time actually doing the operation, the rest of the time is sleeping. Therefore, the likelihood of an interference happening is quite low (since both goroutines would need to be doing the operation simultaneously).

Even if both goroutines are on equal timers, the practical precision of the time library is nowhere near the nanosecond scale required to produce collisions consistently. Plus, the goroutines are doing some printing, which will further diverge the timings.


As pointed out in the comments, your code does still have a data race (as is your intention, seemingly), meaning that we cannot say anything for certain about the output of your program, despite any observations. It's possible that it would output any number from 100-200, or different numbers entirely.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33