观察循环内的无效值

我偶然发现了一个有缺陷的 Golang 代码,该代码试图使用互斥体来防止对 Goroutine 中打印的变量进行更改:


    runtime.GOMAXPROCS(1)


    mutex := new(sync.Mutex)

    for i := 0; i < 10; i++ {

        for j := 0; j < 10; j++ {

            mutex.Lock()

            go func() {

                fmt.Println(i, j, i + j);

                mutex.Unlock()

            }()

        }

    }

我很清楚,互斥体不会直接锁定,而是在下一次迭代中,当值已经增加时锁定。不清楚的是,根据输出,为什么 j 变量达到 10:


...

0 7 7

0 8 8

0 9 9

1 10 11   <--- isn't supposed to be here

...

1 9 10

2 10 12

...

我尝试调试代码,并j = 10在 i 的外循环增加其值时打印。看起来好像外部循环正在释放线程,允许 goroutine 执行并看到无效值 10。有人可以澄清这种行为吗?


拉丁的传说
浏览 89回答 3
3回答

郎朗坤

你有数据竞争。结果未定义。$ go run -race racer.go==================WARNING: DATA RACERead at 0x00c000016110 by goroutine 7:  main.main.func1()      /home/peter/gopath/racer.go:17 +0x7fPrevious write at 0x00c000016110 by main goroutine:  main.main()      /home/peter/gopath/racer.go:14 +0xf1Goroutine 7 (running) created at:  main.main()      /home/peter/gopath/racer.go:16 +0xcd==================0 1 10 2 20 3 30 4 40 5 50 6 60 7 70 8 80 9 9==================WARNING: DATA RACERead at 0x00c000016108 by goroutine 16:  main.main.func1()      /home/peter/gopath/racer.go:17 +0x50Previous write at 0x00c000016108 by main goroutine:  main.main()      /home/peter/gopath/racer.go:13 +0x140Goroutine 16 (running) created at:  main.main()      /home/peter/gopath/racer.go:16 +0xcd==================1 10 111 1 21 2 31 3 41 4 51 5 61 6 71 7 81 8 91 9 102 10 122 1 32 2 42 3 52 4 62 5 72 6 82 7 92 8 102 9 113 10 133 1 43 2 53 3 63 4 73 5 83 6 93 7 103 8 113 9 124 10 144 1 54 2 64 3 74 4 84 5 94 6 104 7 114 8 124 9 135 10 155 1 65 2 75 3 85 4 95 5 105 6 115 7 125 8 135 9 146 10 166 1 76 2 86 3 96 4 106 5 116 6 126 7 136 8 146 9 157 10 177 1 87 2 97 3 107 4 117 5 127 6 137 7 147 8 157 9 168 10 188 1 98 2 108 3 118 4 128 5 138 6 148 7 158 8 168 9 179 10 199 1 109 2 119 3 129 4 139 5 149 6 159 7 169 8 179 9 18Found 2 data race(s)exit status 66$ racer.go:package mainimport (    "fmt"    "runtime"    "sync")func main() {    runtime.GOMAXPROCS(1)    mutex := new(sync.Mutex)    for i := 0; i < 10; i++ {        for j := 0; j < 10; j++ {            mutex.Lock()            go func() {                fmt.Println(i, j, i+j)                mutex.Unlock()            }()        }    }}

子衿沉夜

您存在数据竞争,因此结果未定义。运行它并-race选择查看。当您mutex.Lock()首先在循环体内调用时,不会阻塞。然后,您启动一个 goroutine 来读取i和j,并且主 goroutine 继续进行内循环的下一次迭代,并递增j。然后再次调用lock,这将阻塞直到前一个goroutine完成。但是您已经对j.

繁星淼淼

让我来回答为什么打印时可以得到不可能的j10 。因为当你在循环中使用 goroutine 时,fmt.Println(i, j, i+j)race with i++/j++,你无法确定打印时到底是什么值,而如果j增加到界限,有可能打印出 10。如果你想阻止这场比赛,你可以将i,j作为参数值传递,例如&nbsp; &nbsp; runtime.GOMAXPROCS(1)&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; for j := 0; j < 10; j++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go func(a, b, c int) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(a, b, c);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }(i, j, i+j)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }希望这可以帮助。
打开App,查看更多内容
随时随地看视频慕课网APP