问题

最近在代码中遇到了这么一个问题,现在有一个循环,每一个循环中创建一个协程用来执行函数,我发现函数运行的结果却是大部分时候都是使用最后一个循环变量,不符合实际要求。

大概的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
	"fmt"
	"net/http"
)

func main() {

	for i := 0; i < 10; i++ {
		go func() { fmt.Println(i) }()
	}
	http.ListenAndServe(":8080", nil)
}

运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
10
10
10
10
10
10
10
10
10
10

多次运行,可以发现大部分情况都是将 i = 10 代入函数执行。

原因

那这是为什么呢? 这个就是函数闭包。协程运行的是一个闭包函数,其中使用了主线程的变量i 。看上去这和第一组几乎一样。但是在每个协程中,从进入匿名函数到调用Println将i的值复制入栈之间仍需要一小段时间运行,而这段时间内足以主线程完成全部10次循环。所以终于到将i的值复制入栈调用Println时,i已经成为10且不再变化了。

解决

那该怎么解决使代码如我们的需求运行呢?

我们只需将变量 i 复制进栈中即可,改动后的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
	"fmt"
	"net/http"
)

func main() {

	for i := 0; i < 10; i++ {
		go func(j int) { fmt.Println(j) }(i)
	}
	http.ListenAndServe(":8080", nil)
}

结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
0
4
1
2
3
6
5
7
8
9