为了账号安全,请及时绑定邮箱和手机立即绑定

为什么我的“完成”频道会随机关闭?

为什么我的“完成”频道会随机关闭?

Go
ITMISS 2023-08-14 14:40:11
我构建了以下 go 代码。这个想法是构建一个完成通道和一个生成 int 通道的生成器。将它们链接到一个 2 阶段管道 chanNumbers := pipelineb(done, pipelinea(done, gen(done)))几秒钟后,取消已完成的通道。我希望看到生成器和管道的两级取消并返回,但文本“PipeX 现在终止”仅随机出现,我真的不明白为什么。有人有主意吗?package mainimport (    "fmt"    "time")func gen(done <-chan interface{}) <-chan int {    ret := make(chan int)    cx := 0    go func() {        for {            select {            case <-done:                fmt.Println("**Generator Terminates now")                time.Sleep(2 * time.Second)                fmt.Println("**Generator has terminated now")                close(ret)                return            case ret <- cx:                fmt.Printf("Gen : we push %d \n", cx)                cx = cx + 1            }        }    }()    fmt.Println("Generator has created and returned its channel")    return ret}func pipea(done <-chan interface{}, in <-chan int) <-chan int {    ret := make(chan int)    go func() {        for {            select {            case <-done:                fmt.Println("**pipeA terminates")                time.Sleep(2 * time.Second)                fmt.Println("**pipeA has terminated now")                close(ret)                return            case tmp, ok := (<-in):                if ok {                    fmt.Printf("pipeA : we push %d \n", tmp)                    ret <- tmp                } else {                    in = nil                }            }        }    }()    return ret}func pipeb(done <-chan interface{}, in <-chan int) <-chan int {    ret := make(chan int)    go func() {        for {            select {            case <-done:                fmt.Println("**pipeB terminates")                time.Sleep(2 * time.Second)                fmt.Println("**pipeB has terminated now")                close(ret)
查看完整描述

1 回答

?
收到一只叮咚

TA贡献1821条经验 获得超4个赞

您有四个正在运行的 go 例程:

  1. gen,你的生成器,写入无缓冲的输出通道,直到done

  2. pipeA,从 读取gen,写入无缓冲的输出通道,直到done

  3. pipeB,从 读取pipeA,写入无缓冲的输出通道,直到done

  4. main,从pipeB读到done

现在,当您关闭时done,它完全取决于 go 例程看到它的顺序。

如果main是第一个看到它done已关闭,它将打破 for 循环并停止消耗pipeB。但是如果pipeB仍然尝试写入输出通道(ret <- tmp),它将在那里阻塞;所以它永远不会到达该<- done部分。

有两个选项可以解决此问题:

  1. 只在你的生成器中监听done,并让其他 go 例程使用for n := range in { }.

  2. select将您的发送逻辑也放入 a 中,以便您的生成器和管道可以检测何时done关闭。

或者,您可能希望使用缓冲输出通道,但即使如此,此问题仍然可能发生。


查看完整回答
反对 回复 2023-08-14
  • 1 回答
  • 0 关注
  • 101 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信