【问题标题】:Println in loop and closure output different values循环和闭包中的 Println 输出不同的值
【发布时间】:2014-01-07 14:15:33
【问题描述】:

我有以下代码:

dirs, err      := get_directories(bucket, start_dir, "")

其中 dirs 是一个字符串数组。在那之后,我正在循环它们:

for _, dir     := range dirs {
  fmt.Println("le dir",dir)
  go func() {
    fmt.Println("working on",dir)
    get_files(bucket, dir, "")
    wg.Done()
  }()
}

wg.Wait()

在dirs中,我有["one", "two"],如果看到如下内容:

le dir one
le dir two
working on one
working on one

为什么 gorouting 没有使用正确的 dir 值?

【问题讨论】:

  • 我最初认为这是未定义的行为,但循环的每次迭代都会创建一个独立于前一个的新目录。
  • @Tempus 这讨论了 相反 的行为而不是报告的行为(这让我感到困惑) - 在报告的行为中,first 值在关闭。
  • @Tempus - 我被那个 recently 咬了 - 好电话。
  • @user2864740 “很有可能当您运行此代码时,您会看到每次迭代打印的最后一个元素”——关键字是 chance。调度程序可能不会做出您期望的选择,因此它可能会打印两次 first 值,甚至会打印第一个然后是第二个。

标签: go


【解决方案1】:

您正在创建多个执行线程(goroutines),它们不能保证以任何特定的顺序执行(这是并发的本质)。因此,理论上主线程可以在调度程序调度任何 goroutine 之前完成其循环。这可能就是您正在经历的。

由于主循环在任何 goroutine 执行其第一条指令之前就已完成执行,dir 已到达其最终值,因此每个 goroutine 将使用该值(不是@的值987654324@ 创建闭包时)。

如果您循环的次数更多(dirs 中的元素更多)或每次循环迭代的执行时间更长(例如,如果您在循环中使用Wait()ed),您可能会开始看到一些混合在主例程的打印语句和 goroutine 的打印语句之间。

以下是您的示例的简化、惯用的重写,供其他可能觉得这个问题有用的人使用:

http://play.golang.org/p/6G_8xOi9Fi

package main

import "fmt"

func main() {
    ch1 := make(chan bool)

    // with 1000 iterations, you can probably see some intermingling
    // between lines printed via goroutines and those of your main thread
    for i:=0;i<1000;i++ {
        fmt.Println("before kicking off goroutine:", i)
        go func() {
            fmt.Println("in goroutine:", i)
            ch1 <- true // signal 'done'
        }()
        // alternatively, add a `time.Wait()` here
        // (and reduce the loop iterations) to see
        // intermingling for a different reason.
    }

    <- ch1 // wait for goroutines to complete
}

【讨论】:

  • 不是for循环的每次迭代都创建了自己独立的dir变量吗?我错过了什么吗?
  • @FUZxxl 不,考虑一下:play.golang.org/p/JK49dDBCQ_。当 goroutine 修改计数器时,它也会被主线程修改,并且循环退出。如果每个线程都有自己的副本,那么修改 goroutine 中的值不会影响主线程中的值。
  • 在你的sn-p中,它不会在从频道接收到一个值后停止吗?
猜你喜欢
  • 2021-10-14
  • 1970-01-01
  • 2011-01-27
  • 2023-03-08
  • 2011-05-19
  • 2016-05-23
  • 2020-12-30
  • 2022-08-17
相关资源
最近更新 更多