PrintData
ポインタ受信機であるとtask
値であるため、メソッド呼び出しを行うとき、コンパイラが自動的にtask
のアドレスをとります。結果として得られるコールは(&task).PrintData()
と同じです。
変数task
は、ループの各繰り返しで異なる値に設定されます。最初のゴルーチンは、task
が2番目の値に設定されるまで実行されません。 this exampleを実行して、各繰り返しで同じアドレスがPrintDataに渡されることを確認します。
これを解決する方法はいくつかあります。第二のループの内側に新しい変数を作成することである
tasks := []*Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
go task.PrintData()
}
playground example
:最初のスライスで*Task
を使用することである
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
task := task
go task.PrintData()
}
playground example
第です(自動的に挿入されたアドレス操作を使用して)スライス要素のアドレスを取得します。
tasks := []Task{{"hello", 1}, {"world", 2}}
for i := range tasks {
go tasks[i].PrintData()
}
playground example
さらに別のオプションは自動的にtask
のアドレスを取ってからのメソッド呼び出しを防ぐために、値の受信機に印刷データを変更することです:
func (this Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
playground example
この問題はありますissue discussed in the closures and goroutines FAQと同様です。この問題の違いは、goroutine関数へのポインタを渡すために使用されるメカニズムです。問題のコードは、メソッドのreceiver引数を使用します。 FAQのコードはclosureを使用しています。
ありがとう、構造のメンバーを変更したいので、3番目の方が良いです。 – Devin
@Devin、スライスを変更する場合は、3番目のオプションが唯一の方法です。更新された第3のオプションを参照してください。それはオリジナルよりもあまり冗長ではありません。 –
@Devin Firstオプションは通常、(あなたの意図が 'Task'を変更し、スライス内のポインタを新しいポインタで置き換えるのではない限り)通常動作しますが、3番目のオプションはまだまだ優れています。 – hobbs