func makethumbnail2(filename[]string){ ch:=make(chan struct{}) for _,file:=range filename{ file=file go func(){
fmt.Println(file)
thumbnail.ImageFile(file)
ch<- struct {}{}
}()
}
for range filename{
<-ch
}
} filename 为 100 个图片文件的地址 slice 。 输出是什么。
1
morefreeze 2016-12-30 18:26:42 +08:00
输出不就是每个文件名么
你需要找本书看一眼 |
2
kopp123 OP @morefreeze 不懂别瞎 bb 按照理解应该是输出第 99 个文件路径。但是实际上确实随机输出 4 个文件地址才输出第 99 个文件地址,为啥是这样呢。
|
3
sakeven 2016-12-30 19:02:28 +08:00 1
这段代码输出第 99 个文件地址概率非常大,后面都是。跟循环和 gorountine 的切换有关。
另外 file := file ,就是每个文件了。 |
4
wangxiyu191 2016-12-30 19:17:47 +08:00
@kopp123 那说明前四个负责 printf 的 goroutine 被执行的时候 for 循环还没跑完。
|
5
tyzual 2016-12-30 19:22:14 +08:00
1. 贴代码的时候注意格式。你贴成这样鬼知道代码长啥样
2. 贴代码的时候把无关代码注释掉。比如 thumbnail.ImageFile(file) 3. 你上面贴的那一段代码格式化好以后大概长这样 =========================================== package main import ( "fmt" ) func makethumbnail2(filename []string) { ch := make(chan struct{}) for _, file := range filename { file = file go func() { fmt.Println(file) //thumbnail.ImageFile(file) ch <- struct{}{} }() } for range filename { <-ch } } func main() { var files []string for i := 0; i < 100; i++ { files = append(files, fmt.Sprintf("file%d", i)) } makethumbnail2(files) } ==================================== 4. 下面来解答你的问题 1.1 你在 func() 里面 捕获了 file 变量。而 go 的捕获机制是按照引用捕获。 也就是说,如果我们在 func()外部改变 file 的值, func()内部的 file 也会受到影响 1.2 你的 func()是在另一个 goroutine 里面执行的。也就是说,一方面有 N 多个 func()在不同的 goroutine 里面执行。一方面第一个 for range 循环又在改变 file 的值。在第一个 for range 完成之前,你看到的输出应该是随缘产生的。而 for range 完成之后,则会输出最后一个文件名。 1.3 至于楼主的四个随机文件,那单纯是因为输出了 4 个文件以后,第一个 for range 完成了。 1.4 上面那段测试代码在我的环境下运行只输出了 10 个 file99 1.5 参考资料 https://www.goinggo.net/2014/06/pitfalls-with-closures-in-go.html |
7
kopp123 OP @tyzual 我可以这样理解吗? 可以把 range 循环体代码 和 go routine 代码 放在 2 个地方 。当 go routine 启动第一次执行时 range 已经跑了很多圈而起 go func 对 range 中的循环变量是引用捕获。如果 f:=file go func 中对 f 就是值捕获。
|
8
CRVV 2016-12-30 20:09:40 +08:00
|
9
tyzual 2016-12-30 20:17:21 +08:00
当 go routine 启动第一次执行时 range 已经跑了很多圈而起 go func 对 range 中的循环变量是引用捕获。
这句话可以这样理解 如果 f:=file go func 中对 f 就是值捕获。 go 中只有按引用捕获,所以如果在 go func 中引用 f 还是按引用捕获,只不过捕获的是 f 的引用。 go func 中的值会随着 f 变化而变化。 如果在 go func 中不想让捕获的值随着外部修改而变化的话应该把捕获的值当成一个参数传递。代码大概长这样 ==================== func makethumbnail2(filename []string) { ch := make(chan struct{}) for _, file := range filename { go func(file string) { fmt.Println(file) //thumbnail.ImageFile(file) ch <- struct{}{} }(file) } for range filename { <-ch } } ================================= 注意 go func 多了一个 file string 的参数,而 go func 结束的时候手动把 file 传递进去了。 因为 go func 有一个 file 参数,所以 go func 里面的 fmt.Println(file)的 file 实际上打印的是 go func 的参数。 而 go func 结束的时候在括号中传递的参数 file 是 for range 中当前的 file 。而 string 类型的参数是按照值的类型传递的。所以上面代码就会(无序)输出 file0 - file99 |
10
kopp123 OP @tyzual 你说的给 go func 加的参数方式我知道。实际上如果在 go func 外面 加 f:=file 不给 fo func 加参数也可以实现。
unc makethumbnail2(filename []string) { ch := make(chan struct{}) for _, file := range filename { f:=file go func() { fmt.Println(file) //thumbnail.ImageFile() ch <- struct{}{} }() } for range filename { <-ch } } |
11
zts1993 2016-12-31 00:18:17 +08:00 via Android
结果看这里 thumbnail.ImageFile(file)
ch 这里只是等待所有 goroutine 完成而已 |
12
reus 2016-12-31 03:03:38 +08:00
应该用 file := file ,这样才会新建一个变量,不然就一直用 for 里的循环变量了。 for + go 的新手常见错误……
|
13
scnace 2016-12-31 11:10:33 +08:00
我觉得这样子比较好理解
[PlayGround]( https://play.golang.org/p/8Uh3CvraIQ) 另, 楼主的`file=file`加这句代码的意义是什么? 再另,楼主 10 楼的代码里面 应该是`fmt.Println(f)`才会有遍历的效果就是了。。。 |