原文地址:https://github.com/golang/go/wiki/CommonMistakes#wiki-pages-box
Introduction(介紹)
When new programmers start using Go or when old Go programmers start using a new concept, there are some common mistakes that many of them make. Here is a non-exhaustive list of some frequent mistakes that show up on the mailing lists and in IRC.Using goroutines on loop iterator variables
當工程師剛開始使用Go或者Go工程師剛開始接觸到一個新的概念的時候,他們中的很多人對這些概念會產生類似的誤解。下文就郵件清單和IRC中一些常見的問題給出簡要的解釋。迭代變數用在迴圈下的go協程上。
When iterating in Go, one might attempt to use goroutines to process data in parallel. For example, you might write something like this, using a closure:
當使用Go語言處理迭代時,有人可能會嘗試使用協程來平行處理資料。例如,你可能會使用閉包寫下面的代碼:
for _, val := range values { go func() { fmt.Println(val) }()}
The above for loops might not do what you expect because their val variable is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.
以上的代碼或許不會按照你的預期運行,因為他們的val變數實際上是值為每一個切片元素值得單一變數。因為這些閉包都綁定到了這一個變數,那麼很可能當你執行這段代碼得時候每次迭代你都只看到列印出來得序列中最後一個數值,因為協程很可能知道迴圈結束之後才會執行。
The proper way to write that closure loop is:
正確的編寫閉包迴圈的方式應該是:
for _, val := range values { go func(val interface{}) { fmt.Println(val) }(val)}
By adding val as a parameter to the closure, val is evaluated at each iteration and placed on the stack for the goroutine, so each slice element is available to the goroutine when it is eventually executed.
通過將val作為一個參數添加到閉包裡頭,val被取值並存放至協程的棧上,所以當協程最終執行的時候,每一個切片元素對協程來說都是可用的。
It is also important to note that variables declared within the body of a loop are not shared between iterations, and thus can be used separately in a closure. The following code uses a common index variable i to create separate vals, which results in the expected behavior:
同樣值得關注的是,在迴圈體中被聲明的變數並不在每次的迭代中共用,因此可以被單獨的用到閉包當中。下面的代碼使用了一個共同的索引變數i來建立單獨的val,這樣會產生預期的結果。
for i := range valslice { val := valslice[i] go func() { fmt.Println(val) }()}
Note that without executing this closure as a goroutine, the code runs as expected. The following example prints out the integers between 1 and 10.
注意下在沒有將閉包用協程來執行時,代碼會按照預期執行。下面的例子列印出了數字1到10.
for i := 1; i <= 10; i++ { func() { fmt.Println(i) }()}
Even though the closures all still close over the same variable (in this case, i), they are executed before the variable changes, resulting in the desired behavior.
即使這些閉包們仍然綁定到同樣的變數(在這個例子中),他們卻在變數改變之前執行,產生了想要的結果。
Another similar situation that you may find like following:
另外一個類似的情境例如如下:
for _, val := range values { go val.MyMethod()}func (v *val) MyMethod() { fmt.Println(v)}
The above example also will print last element of values, the reason is same as closure. To fix the issue declare another variable inside the loop.
上面的例子同樣列印values裡的最後一個元素,原因和閉包是一樣的。為了修複這個問題,可以在迴圈裡頭聲明另一個變數。
for _, val := range values { newVal := val go newVal.MyMethod()}func (v *val) MyMethod() { fmt.Println(v)}