2017-02-10 85 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共享它們。