2017-02-10 39 views
3

我有这样的简单的一段代码(或此处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. 没有'1的j
  2. 有“5的用于j
  3. 只有3个值,对于i = 1,代替4-

我可以理解缺乏” 1',因为它被写入之前的变量被递增。

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

回答

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) 

然后,它会在ij的值传递你够程开始的时间,后续的迭代不会影响到它:https://play.golang.org/p/P3kUP5e1Fp

+0

请注意,您错过了最后的'4 + 4',因为您并未等待最后的goroutine完成。 https://play.golang.org/p/lPQWfvAV5S – JimB

+0

我知道,传递'i'和'j'作为函数参数会有帮助。我错过了'for'循环的基础知识,即。该变量首先递增,然后检查THEN条件。这解释了5s –

1

当您执行这一点:

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

当前goroutine使另一个增加j的循环。

增量方向具有printf这就是为什么即使想到了函数被调用,同时
j< 5它可提高到5功能来得及打印出值之前之前进行。
换句话说,你喜欢这个程序运行:

  • 输入回路
  • 锁定互斥
  • 呼叫FUNC()
  • 增量Ĵ
  • 打印值
  • 解锁互斥

解决这个问题的方法是通过值es的价值,而不是在整个goroutines共享它们。