Golang:循环下的闭包(翻译)

原文地址:https://github.com/golang/go/wiki/CommonMistakes#wiki-pages-box
)

Introduction(介绍)

When new programmers start using Go or when old Go programmers start using a new concept, there are some common mistakes that many of them make. Here is a non-exhaustive list of some frequent mistakes that show up on the mailing lists and in IRC.Using goroutines on loop iterator variables

当工程师刚开始使用Go或者Go工程师刚开始接触到一个新的概念的时候,他们中的很多人对这些概念会产生类似的误解。下文就邮件列表和IRC中一些常见的问题给出简要的解释。迭代变量用在循环下的go协程上。

When iterating in Go, one might attempt to use goroutines to process data in parallel. For example, you might write something like this, using a closure:

当使用Go语言处理迭代时,有人可能会尝试使用协程来并行处理数据。例如,你可能会使用闭包写下面的代码:

for _, val := range values {
    go func() {
        fmt.Println(val)
    }()
}

The above for loops might not do what you expect because their val variable is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.

以上的代码或许不会按照你的预期运行,因为他们的val变量实际上是值为每一个切片元素值得单一变量。因为这些闭包都绑定到了这一个变量,那么很可能当你执行这段代码得时候每次迭代你都只看到打印出来得序列中最后一个数值,因为协程很可能知道循环结束之后才会执行。

The proper way to write that closure loop is:

正确的编写闭包循环的方式应该是:

for _, val := range values {
    go func(val interface{}) {
        fmt.Println(val)
    }(val)
}

By adding val as a parameter to the closure, val is evaluated at each iteration and placed on the stack for the goroutine, so each slice element is available to the goroutine when it is eventually executed.

通过将val作为一个参数添加到闭包里头,val被取值并存放至协程的栈上,所以当协程最终执行的时候,每一个切片元素对协程来说都是可用的。

It is also important to note that variables declared within the body of a loop are not shared between iterations, and thus can be used separately in a closure. The following code uses a common index variable i to create separate vals, which results in the expected behavior:

同样值得关注的是,在循环体中被声明的变量并不在每次的迭代中共享,因此可以被单独的用到闭包当中。下面的代码使用了一个共同的索引变量i来创建单独的val,这样会产生预期的结果。

for i := range valslice {
    val := valslice[i]
    go func() {
        fmt.Println(val)
    }()
}

Note that without executing this closure as a goroutine, the code runs as expected. The following example prints out the integers between 1 and 10.

注意下在没有将闭包用协程来执行时,代码会按照预期执行。下面的例子打印出了数字1到10.

for i := 1; i <= 10; i++ {
    func() {
        fmt.Println(i)
    }()
}

Even though the closures all still close over the same variable (in this case, i), they are executed before the variable changes, resulting in the desired behavior.

即使这些闭包们仍然绑定到同样的变量(在这个例子中),他们却在变量改变之前执行,产生了想要的结果。

Another similar situation that you may find like following:

另外一个类似的场景例如如下:

for _, val := range values {
    go val.MyMethod()
}

func (v *val) MyMethod() {
        fmt.Println(v)
}

The above example also will print last element of values, the reason is same as closure. To fix the issue declare another variable inside the loop.

上面的例子同样打印values里的最后一个元素,原因和闭包是一样的。为了修复这个问题,可以在循环里头声明另一个变量。

for _, val := range values {
        newVal := val
    go newVal.MyMethod()
}

func (v *val) MyMethod() {
        fmt.Println(v)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容