【问题标题】:Go routine ending mysteriously, channel closed without reaching close statementGoroutine 神秘结束,channel 没有达到 close 语句就关闭了
【发布时间】:2021-10-11 10:02:05
【问题描述】:

我创建了以下简单程序来测试使用通道的扇入扇出模式。它所做的是生成一些 go 例程来计算来自输入通道的数字的平方并将平方发送到输出通道。然后将所有输出通道合并到一个通道中以打印main中的正方形。

func calculateSquare(in <-chan int) <-chan int {
    out := make(chan int)

    go func() {
       for num := range in {
           fmt.Printf("Receving num %v\n", num)
           out <- num * num
           fmt.Printf("Sending square %v\n", num * num)
       }
       fmt.Println("Closing out")
       close(out)
    }()

    return out
}

func fanOut(in <-chan int, workerCount int) []<-chan int {
    outs := make([]<-chan int, 0, workerCount)

    for i := 0 ; i < workerCount ; i++ {
        outs = append(outs, calculateSquare(in))
    }

    return outs
}

func fanIn(outs []<-chan int) <-chan int {
    var wg sync.WaitGroup

    merge := make(chan int)

    for _, out := range outs {
        wg.Add(1)

        go func() {
            for result := range out {
                merge <- result
            }

            wg.Done()
        }()
    }

    go func() {
        wg.Wait()
        fmt.Println("Closing merge")
        close(merge)
    }()

    return merge
}

func main() {
    in := make(chan int)

    go func() {
        for i := 0 ; i < 4 ; i++ {
            fmt.Printf("Sending num %v\n", i)
            in <- i
        }
        close(in)
    }()

    outs := fanOut(in, 5)
    merge := fanIn(outs)

    for num := range merge {
        fmt.Printf("Final square %v\n", num)
    }
}

main 函数中,我将 4 个数字 0 -> 3 发送到输入通道,我希望在控制台中看到 4 个正方形。然而,当我运行程序时,即使输出有点波动,但我从未在控制台中看到打印过 4 个平方数。

下面是我看到的示例输出。

Sending num 0
Sending num 1
Sending num 2
Sending num 3
Closing out
Receving num 0
Receving num 1
Receving num 2
Sending square  4
Closing out
Receving num 3
Final square 4
Closing merge

如果有人能向我解释为什么打印了Receving num 1Sending square 1 永远不会出现,我将不胜感激。另外,如果Sending square 1没有打印出来,output这个通道是怎么关闭的。我只看到 2 个Closing out,但是,我正在合并结果的等待组结束了它的Wait()

我一定是哪里做错了。

【问题讨论】:

  • 运行go vet(或运行任何测试,然后运行go vet),你会看到问题:golang.org/doc/faq#closures_and_goroutines
  • @JimB 谢谢你的指点。几天前我读到了这个常见的错误,但我仍然犯了。多么新手的错误:D

标签: go goroutine


【解决方案1】:

修复:

for _, out := range outs {
    wg.Add(1)

    out := out // <- add this

为什么?

https://golang.org/doc/effective_go 是一个很好的资源,涵盖了 channels section 末尾的确切关闭错误(@JimB 提到的):

写起来可能有点奇怪

请求 := 请求

但在 Go 中这样做是合法且惯用的。你得到一个 新版本的同名变量,故意 在本地隐藏循环变量,但对每个 goroutine 都是唯一的。

【讨论】:

    【解决方案2】:

    您的问题出在下面的代码中,fanIn 函数中的 for 循环。

        for _, out := range outs {
            wg.Add(1)
    
            go func() {
                for result := range out {
                    merge <- result
                }
    
                wg.Done()
            }()
        }
    

    这是因为你在 gofunc 中使用了out 迭代器变量,当 gofunc 使用它时,循环结束了。

    这在go/wiki/CommonMistakes子主题Using goroutines on loop iterator variables下进行了描述

    更多示例 - 阅读this

    修正后的循环应该如下,

        for _, out := range outs {
            wg.Add(1)
    
            go func(c <- chan int) {
                for result := range c {
                    merge <- result
                }
    
                wg.Done()
            }(out)
        }
    

    【讨论】:

      猜你喜欢
      • 2021-02-12
      • 1970-01-01
      • 1970-01-01
      • 2013-06-05
      • 2019-02-06
      • 1970-01-01
      • 1970-01-01
      • 2021-04-03
      • 2014-02-03
      相关资源
      最近更新 更多