💻
for...range内でgoroutineを起動する場合注意すること
結論
for...range
で取り出した値をgoroutineで使用する場合は関数の引数経由で参照する
実験
以下のようにfor...range
で取り出した値をクロージャ内でそのまま参照すると、意図したものとは別のデータが参照されてしまう。
これはgoroutineの起動よりも早くループが回ってしまうために、クロージャ内では毎回tasks
の最後の値が参照されてしまっている。
package main
import (
"fmt"
"time"
)
func main() {
tasks := []string{
"first!",
"second!!",
"third!!!",
}
for _, task := range tasks {
go func() {
fmt.Println(task)
}()
}
time.Sleep(time.Second)
}
$ go run main.go
third!!!
third!!!
third!!!
以下のように引数経由で参照すれば解決できる。
package main
import (
"fmt"
"time"
)
func main() {
tasks := []string{
"first!",
"second!!",
"third!!!",
}
for _, task := range tasks {
go func(task string) {
fmt.Println(task)
}(task)
}
time.Sleep(time.Second)
}
$ go run main.go
first!
second!!
third!!!
Discussion
task := task
で shadowing する手もあります。https://go.dev/play/p/49nvpxn0uN0
なるほど、goroutine起動前にtaskの値を確定させてしまえばいいんですね。
go言語勉強中なので参考になります!