英文:
Closures in go routines have incorrect scope unless the variables are copied
问题
我在作为goroutine运行的函数中看到了错误的值。除非将这些值复制到新的变量中,否则它们似乎无法捕获调用它们的作用域中的值。
http://play.golang.org/p/YZYi-IVuYm
与
http://play.golang.org/p/z88G99XSi6
英文:
I am seeing incorrect values in the functions run as goroutines. They don't seem to be capturing the values from the scope in which they were invoked unless copied into new variables.
http://play.golang.org/p/YZYi-IVuYm
vs.
答案1
得分: 14
你可以通过两种方式解决这个问题:
- 在本地上下文中重新分配变量,以便闭包可以捕获值:
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
theIdx, theItem := idx, item
go func() {
fmt.Println(theIdx, theItem)
}()
}
time.Sleep(time.Second)
}
- 将值传递给 goroutine,并在函数中添加参数:
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
go func(idx, item int) {
fmt.Println(idx, item)
}(idx, item)
}
time.Sleep(time.Second)
}
英文:
You either need to re-assign the variable in the local context so that the closure can capture the values:
http://play.golang.org/p/-NO4S4qCZf
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
theIdx, theItem := idx, item
go func() {
fmt.Println(theIdx, theItem)
}()
}
time.Sleep(time.Second)
}
or you pass the values to the goroutine and add parameters to the function
http://play.golang.org/p/5gNToDWSQR
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
go func(idx, item int) {
fmt.Println(idx, item)
}(idx, item)
}
time.Sleep(time.Second)
}
答案2
得分: 9
这是可以预料到的,并且在Go语言的“常见错误”页面中有记录。你可以对设计决策提出异议,但这是一个已知的影响。
推荐的做法是将这些值作为参数传递进去。
英文:
This is expected, and documented in the Go "Common Mistakes" page. You can argue with the design decision, but it is a known effect.
A recommended way to do this is to pass the values in as parameters.
答案3
得分: 2
第一个goroutine执行时,idx
和item
已经被下一个值覆盖了。由于这些例程在同一作用域内执行,你会得到新的值。
事后看来这是有道理的。
英文:
By the time the first goroutine is executed, idx
and item
have already been overwritten with the next values. As the routines are executed within the same scope, you get the new values.
It does make sense, in hindsight.
答案4
得分: 0
请注意,将指针作为参数传递给Go协程仍然存在问题:
不起作用的代码示例:
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
go func(idx int, itemAddr *int) {
fmt.Println(idx, *itemAddr)
}(idx, &item)
}
time.Sleep(time.Second)
}
因此,在这种情况下,重新分配本地变量是解决方法:
起作用的代码示例:
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
theIdx, theItem := idx, item
go func() {
theItemAddr := &theItem
fmt.Println(theIdx, *theItemAddr)
}()
}
time.Sleep(time.Second)
}
以上是要翻译的内容。
英文:
Note that passing pointers in as parameters to a go routine is still problematic:
Does NOT work:
https://play.golang.org/p/kAnJsIJCyNF
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
go func(idx int, itemAddr *int) {
fmt.Println(idx, *itemAddr)
}(idx, &item)
}
time.Sleep(time.Second)
}
So reassigning variables locally is the way to go in this case:
WORKS:
https://play.golang.org/p/wgNFDi5TN-9
package main
import "fmt"
import "time"
func main() {
l := []int{1, 2, 3}
for idx, item := range l {
theIdx, theItem := idx, item
go func() {
theItemAddr := &theItem
fmt.Println(theIdx, *theItemAddr)
}()
}
time.Sleep(time.Second)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论