Why does this code cause data race?

前端 未结 2 1058
被撕碎了的回忆
被撕碎了的回忆 2021-01-21 13:32
1 package main
2
3 import \"time\"
4
5 func main() {
6     m1 := make(map[string]int)
7     m1[\"hello\"] = 1
8     m1[\"world\"] = 2
9     go func() {
10         for i          


        
相关标签:
2条回答
  • 2021-01-21 13:44

    The requirements to have a data race are:

    1. Multiple goroutines accessing the same resource (e.g. a variable) concurrently.
    2. At least one of those accesses is a write.
    3. The accesses are uncynchronized.

    In your code all 3 requirements are met:

    1. You have the main goroutine accesssing m1, and the one you start in it also accesses m1. The main goroutine accesses it after the other goroutine has been launched, so they are concurrent.
    2. The main goroutine writes m1 in line #17: m1 = m2.
    3. The accesses are not synchronized, you use no mutex or channels or anything like that (sleeping is not synchronization).

    Therefore it's a data race.

    The obvious data race is between lines #11 reading m1, and line #17 writing m1.

    But! Since line #17 assigns m2 to m1, then when/if the launched goroutine continues to run, it attempts to read m1 which may now be the value of m2 because we assigned m2 to m1. What does this mean? This introduces another data race writing m2 and reading m1.

    That is after line #17 if the program does not end immediately (it may, but not necessarily), then the launched goroutine attempts to read from m1 which is now m2 which was last written in line #16, so this explains the "conflict" between lines #11 and #16.

    The full go run -race output is as follows:

    ==================
    WARNING: DATA RACE
    Write at 0x00c42000e010 by main goroutine:
      main.main()
          /home/icza/gows/src/play/play2.go:17 +0x22f
    
    Previous read at 0x00c42000e010 by goroutine 5:
      [failed to restore the stack]
    
    Goroutine 5 (running) created at:
      main.main()
          /home/icza/gows/src/play/play2.go:9 +0x190
    ==================
    ==================
    WARNING: DATA RACE
    Read at 0x00c42007e000 by goroutine 5:
      runtime.mapaccess1_faststr()
          /usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
      main.main.func1()
          /home/icza/gows/src/play/play2.go:11 +0x7a
    
    Previous write at 0x00c42007e000 by main goroutine:
      runtime.mapassign_faststr()
          /usr/local/go/src/runtime/hashmap_fast.go:598 +0x0
      main.main()
          /home/icza/gows/src/play/play2.go:16 +0x1fc
    
    Goroutine 5 (running) created at:
      main.main()
          /home/icza/gows/src/play/play2.go:9 +0x190
    ==================
    ==================
    WARNING: DATA RACE
    Read at 0x00c420080088 by goroutine 5:
      main.main.func1()
          /home/icza/gows/src/play/play2.go:11 +0x90
    
    Previous write at 0x00c420080088 by main goroutine:
      main.main()
          /home/icza/gows/src/play/play2.go:16 +0x212
    
    Goroutine 5 (running) created at:
      main.main()
          /home/icza/gows/src/play/play2.go:9 +0x190
    ==================
    Found 3 data race(s)
    exit status 66
    
    0 讨论(0)
  • 2021-01-21 13:51

    Data race

    A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write.

    Instructions Reorder

    compilers and processors may reorder the reads and writes executed within a single goroutine as far as the reordering does not change the behaviour within the routine, it doesn' ensure behaviour of other goroutines to be unaffected

    m2["hello"] = 3
    m1 = m2
    

    Can be reordered to

    m1 = m2
    m2["hello"] = 3
    

    That won't alter the behaviour of the main routine and thus race checking will consider that also for evaluating race condition. Now we have m2["hello"] = 3 causing the race condition and it prints out the same with its original line number

    0 讨论(0)
提交回复
热议问题