// 借用例子
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []field{{"one"},{"two"},{"three"}}
// 例一
for _,v := range data {
// 解決辦法:新增如下語句
// v := v
go v.print()
}
// goroutines print: three, three, three
// for 裡面的 v := v 是解決這問題的一種方案
time.Sleep(3 * time.Second)
// 注意data2是指標陣列
data2 := []*field{{"one"}, {"two"}, {"three"}}
// 例二
for _, v := range data2 {
// go執行是函式,函式執行之前,函式的接受物件已經傳過來
go v.print()
}
// goroutines print: one, two, three
time.Sleep(3 * time.Second)
}
上面的程式碼複製於網路後並在註釋上進行了新增,關於例一為什麼會出來這種現象,也看了下網上的答案,答案不少,大部分都只是提到了類似 for
是 copy 方式或者 for
裡面賦值給另一個變數等,這兩種類似的解釋都不完善或者說只講到了一部分
例一原因解析:
1、賦值:for _,v := range data {...}
變數 v 是通過賦值的方式得到
2、條件作用域:for _,v := range data {...}
for 到 { 之間的變數為條件作用域,條件作用域也可叫隱式作用域
3、區域性作用域:for {}
,花括號裡面的變數為區域性作用域
// 例一
data := []*field{{"one"},{"two"},{"three"}}
for _,v := range data {
// 解決辦法:新增如下語句
go v.print()
}
// goroutines print: three, three, three
// 核心知識點:變數作用域、變數地址與值的關係
// go v.print() 這裡是呼叫一個協程,非阻塞。因程式 go v.print() 變數 v 使用的是條件作用域的變數(區域性變數呼叫父級變數,迴圈第一次的v與迴圈n次後的v都是同一個變數同一地址),結合原因 1,後賦值覆蓋前面賦值,所以 go v.print() 結果是取決於 go 這個協程與 for-range 之間的執行速度,如果協程的執行速度遠快於 for-range 一次的速度,那麼 for-range 每次迴圈 go v.print() 的結果都是當前 for-range v 的值,如果 for-range 一次的速度遠快於一次協程的速度,那麼 for-range 每次迴圈 go v.print() 的值有可能是當前 for-range 的值也可能會是後面 for-range v 的值,例子:假設 for-range 的速度是 go v.print() 的 10 倍,data 裡面有 1-20 個 int, len 為 20 的切片,go v.print() 的結果是前面 10 個為 10,後面 10 個為 20
回到家以後,給還在公司的女朋友發個微信,給你準備了個禮物(家就是個地址),剛開始準備的玫瑰,想到女朋友對荷花也非常喜歡,又換成了荷花,想到她也非常喜歡月季,又換成了月季,送那麼比較好呢?換了不知多少次,到換成了項鍊時,她屁顛屁顛的回來了,這時她收到的禮物是項鍊。在這個過程中,地址始終未變,禮物一直在變化,女朋友收到的禮物解決於她到家時間(禮物是變數,家為禮物的地址,月季或玫瑰或項鍊為禮物的值)
本作品採用《CC 協議》,轉載必須註明作者和本文連結