为了账号安全,请及时绑定邮箱和手机立即绑定

观察循环内的无效值

观察循环内的无效值

Go
拉丁的传说 2023-07-17 14:19:41
我偶然发现了一个有缺陷的 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 70 8 80 9 91 10 11   <--- isn't supposed to be here...1 9 102 10 12...我尝试调试代码,并j = 10在 i 的外循环增加其值时打印。看起来好像外部循环正在释放线程,允许 goroutine 执行并看到无效值 10。有人可以澄清这种行为吗?
查看完整描述

3 回答

?
郎朗坤

TA贡献1921条经验 获得超9个赞

你有数据竞争。结果未定义。


$ go run -race racer.go

==================

WARNING: DATA RACE

Read at 0x00c000016110 by goroutine 7:

  main.main.func1()

      /home/peter/gopath/racer.go:17 +0x7f


Previous write at 0x00c000016110 by main goroutine:

  main.main()

      /home/peter/gopath/racer.go:14 +0xf1


Goroutine 7 (running) created at:

  main.main()

      /home/peter/gopath/racer.go:16 +0xcd

==================

0 1 1

0 2 2

0 3 3

0 4 4

0 5 5

0 6 6

0 7 7

0 8 8

0 9 9

==================

WARNING: DATA RACE

Read at 0x00c000016108 by goroutine 16:

  main.main.func1()

      /home/peter/gopath/racer.go:17 +0x50


Previous write at 0x00c000016108 by main goroutine:

  main.main()

      /home/peter/gopath/racer.go:13 +0x140


Goroutine 16 (running) created at:

  main.main()

      /home/peter/gopath/racer.go:16 +0xcd

==================

1 10 11

1 1 2

1 2 3

1 3 4

1 4 5

1 5 6

1 6 7

1 7 8

1 8 9

1 9 10

2 10 12

2 1 3

2 2 4

2 3 5

2 4 6

2 5 7

2 6 8

2 7 9

2 8 10

2 9 11

3 10 13

3 1 4

3 2 5

3 3 6

3 4 7

3 5 8

3 6 9

3 7 10

3 8 11

3 9 12

4 10 14

4 1 5

4 2 6

4 3 7

4 4 8

4 5 9

4 6 10

4 7 11

4 8 12

4 9 13

5 10 15

5 1 6

5 2 7

5 3 8

5 4 9

5 5 10

5 6 11

5 7 12

5 8 13

5 9 14

6 10 16

6 1 7

6 2 8

6 3 9

6 4 10

6 5 11

6 6 12

6 7 13

6 8 14

6 9 15

7 10 17

7 1 8

7 2 9

7 3 10

7 4 11

7 5 12

7 6 13

7 7 14

7 8 15

7 9 16

8 10 18

8 1 9

8 2 10

8 3 11

8 4 12

8 5 13

8 6 14

8 7 15

8 8 16

8 9 17

9 10 19

9 1 10

9 2 11

9 3 12

9 4 13

9 5 14

9 6 15

9 7 16

9 8 17

9 9 18

Found 2 data race(s)

exit status 66

racer.go:


package main


import (

    "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()

            }()

        }

    }

}


查看完整回答
反对 回复 2023-07-17
?
子衿沉夜

TA贡献1828条经验 获得超3个赞

您存在数据竞争,因此结果未定义。运行它并-race选择查看。

当您mutex.Lock()首先在循环体内调用时,不会阻塞。然后,您启动一个 goroutine 来读取ij,并且主 goroutine 继续进行内循环的下一次迭代,并递增j。然后再次调用lock,这将阻塞直到前一个goroutine完成。

但是您已经对j.


查看完整回答
反对 回复 2023-07-17
?
繁星淼淼

TA贡献1775条经验 获得超11个赞

让我来回答为什么打印时可以得到不可能的j10 。


因为当你在循环中使用 goroutine 时,fmt.Println(i, j, i+j)race with i++/j++,你无法确定打印时到底是什么值,而如果j增加到界限,有可能打印出 10。


如果你想阻止这场比赛,你可以将i,j作为参数值传递,例如


    runtime.GOMAXPROCS(1)


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

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

            go func(a, b, c int) {

                fmt.Println(a, b, c);

            }(i, j, i+j)

        }

    }

希望这可以帮助。


查看完整回答
反对 回复 2023-07-17
  • 3 回答
  • 0 关注
  • 86 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信