循环内的互斥导致意外输出

时间:2017-02-10 15:21:53

标签: go concurrency

我有这段简单的代码(或https://play.golang.org/p/KW8_OHUp9v

package main

import (
    "fmt"
    "sync"
)

func main() {
    mutex := new(sync.Mutex)

    for i := 1; i < 5; i++ {
        for j := 1; j < 5; j++ {
            mutex.Lock()
            go func() {
                fmt.Printf("%d + %d = %d\n", i, j, j+i)
                mutex.Unlock()
            }()
        }
    }
}

它产生这样的输出

1 + 2 = 3
1 + 3 = 4
1 + 4 = 5
2 + 5 = 7
2 + 2 = 4
2 + 3 = 5
2 + 4 = 6
3 + 5 = 8
3 + 2 = 5
3 + 3 = 6
3 + 4 = 7
4 + 5 = 9
4 + 2 = 6
4 + 3 = 7
4 + 4 = 8

Program exited.

看看输出我很惊讶:

  1. j
  2. 没有'1'
  3. j
  4. 有5个
  5. i = 1只有3个值,而不是4个
  6. 我可以理解缺少'1',因为变量在写入之前会递增。

    有人可以解释2.和3.?

2 个答案:

答案 0 :(得分:3)

您正在关闭循环中的变量,然后在单独的线程中运行闭包,而这些变量会继续更改。当你这样做时,期望意外 - 例如,你看到5s因为j在最后一次迭代时增加到5,这导致循环结束,但j仍然保持5,这是单独的然后线程可以读取。它与你的互斥锁无关;它是变量的跨线程共享。如果您使用:

go func(i,j int) {
    fmt.Printf("%d + %d = %d\n", i, j, j+i)
    mutex.Unlock()
}(i,j)

然后它会在你的goroutine启动时传递ij的值,后续迭代不会影响它:https://play.golang.org/p/P3kUP5e1Fp

答案 1 :(得分:1)

执行此操作时:

go func() {
                fmt.Printf("%d + %d = %d\n", i, j, j+i)
                mutex.Unlock()
            }()

当前goroutine创建另一个循环,增加j。

增量发生在printf之前,这就是为什么即使认为函数被调用的原因 j < 5 $(function() { // prevent the submit button to be pressed twice $(".createForm").submit(function() { $(this).find('.submit').attr('disabled', true); $(this).find('.submit').text('Sending, please wait...'); }); })可以在函数有时间打印出值之前将其增加到5 换句话说,你的程序运行如下:

  • 输入循环
  • 锁定互斥锁
  • 调用func()
  • 增量j
  • 打印值
  • 解锁Mutex

解决这个问题的方法是将值按值传递给函数,而不是在整个goroutine中共享它们。