一段 golang 代码不太懂就 golang 大牛解读 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
kopp123
V2EX    Go 编程语言

一段 golang 代码不太懂就 golang 大牛解读

  •  
  •   kopp123 2016-12-30 18:09:09 +08:00 3230 次点击
    这是一个创建于 3296 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 。 输出是什么。

    13 条回复    2016-12-31 11:10:33 +08:00
    morefreeze
        1
    morefreeze  
       2016-12-30 18:26:42 +08:00
    输出不就是每个文件名么
    你需要找本书看一眼
    kopp123
        2
    kopp123  
    OP
       2016-12-30 18:46:27 +08:00
    @morefreeze 不懂别瞎 bb 按照理解应该是输出第 99 个文件路径。但是实际上确实随机输出 4 个文件地址才输出第 99 个文件地址,为啥是这样呢。
    sakeven
        3
    sakeven  
       2016-12-30 19:02:28 +08:00   1
    这段代码输出第 99 个文件地址概率非常大,后面都是。跟循环和 gorountine 的切换有关。

    另外 file := file ,就是每个文件了。
    wangxiyu191
        4
    wangxiyu191  
       2016-12-30 19:17:47 +08:00
    @kopp123 那说明前四个负责 printf 的 goroutine 被执行的时候 for 循环还没跑完。
    tyzual
        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
    kopp123
        6
    kopp123  
    OP
       2016-12-30 19:37:56 +08:00
    @sakeven 你是对的。
    kopp123
        7
    kopp123  
    OP
       2016-12-30 19:48:13 +08:00
    @tyzual 我可以这样理解吗? 可以把 range 循环体代码 和 go routine 代码 放在 2 个地方 。当 go routine 启动第一次执行时 range 已经跑了很多圈而起 go func 对 range 中的循环变量是引用捕获。如果 f:=file go func 中对 f 就是值捕获。
    CRVV
        8
    CRVV  
       2016-12-30 20:09:40 +08:00
    https://golang.org/doc/effective_go.html#channels

    和楼主问的问题完全一样的例子
    tyzual
        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
    kopp123
        10
    kopp123  
    OP
       2016-12-30 22:56:51 +08:00
    @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
    }
    }
    zts1993
        11
    zts1993  
       2016-12-31 00:18:17 +08:00 via Android
    结果看这里 thumbnail.ImageFile(file)

    ch 这里只是等待所有 goroutine 完成而已
    reus
        12
    reus  
       2016-12-31 03:03:38 +08:00
    应该用 file := file ,这样才会新建一个变量,不然就一直用 for 里的循环变量了。 for + go 的新手常见错误……
    scnace
        13
    scnace  
       2016-12-31 11:10:33 +08:00
    我觉得这样子比较好理解

    [PlayGround]( https://play.golang.org/p/8Uh3CvraIQ)

    另, 楼主的`file=file`加这句代码的意义是什么?
    再另,楼主 10 楼的代码里面 应该是`fmt.Println(f)`才会有遍历的效果就是了。。。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5201 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 79ms UTC 06:53 PVG 14:53 LAX 22:53 JFK 01:53
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86