问题描述
import "fmt"
import "time"
func main() {
array := []int{1,2,3}
for _,num := range array {
go func() {
fmt.Println(fucknum)
}(fucknum)
time.Sleep(time.Nanosecond)
}
time.Sleep(time.Second)
}
由于for循环中有一个time.Sleep
,因此我期望输出为1 2 3
,因为每个time.Sleep
的执行量都很大。
但是,这段代码的输出为2 1 3
。在将Nanosecond
更改为Microsecond
之后,它变成了1 2 3
。
为了进行比较,我还测试了python3的asyncio
版本,在该版本中,我假设asyncio.call_soon
等同于Go的 non-IO 协程。
import asyncio
loop = asyncio.get_event_loop()
async def @R_404_4876@:
for i in range(1,4):
# replace call_soon with asyncio.sleep(0) does not change the result
loop.call_soon(lambda : print(i))
await asyncio.sleep(0)
loop.run_until_complete(@R_404_4876@)
输出始终为1 2 3
(此输出与我所期望的相同,因为我知道在内部将call_soon调度的函数简单地添加到FIFO队列中)
如何解释Go版本的行为?
解决方法
由于每个生成的goroutine都在访问其作用域之外定义的相同变量,因此它们没有分配新的内存地址,而是引用了相同的地址。您的示例中的for循环实际上多次引用相同的变量。通过在goroutine定义中引入局部作用域,每次生成新的goroutine时,它将在每次迭代中分配一个新变量。
要解决此问题,您需要将索引作为关闭函数的参数传递。
这应该可以解决您的问题。
func main() {
array := []int{1,2,3}
for _,num := range array {
go func(num int) {
fmt.Println(num)
}(num)
time.Sleep(time.Nanosecond)
}
time.Sleep(time.Second)
}