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

我构建了以下 go 代码。


这个想法是构建一个完成通道和一个生成 int 通道的生成器。将它们链接到一个 2 阶段管道 chanNumbers := pipelineb(done, pipelinea(done, gen(done)))


几秒钟后,取消已完成的通道。我希望看到生成器和管道的两级取消并返回,但文本“PipeX 现在终止”仅随机出现,我真的不明白为什么。有人有主意吗?


package main

import (

    "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)

ITMISS
浏览 60回答 1
1回答

收到一只叮咚

您有四个正在运行的 go 例程:gen,你的生成器,写入无缓冲的输出通道,直到donepipeA,从 读取gen,写入无缓冲的输出通道,直到donepipeB,从 读取pipeA,写入无缓冲的输出通道,直到donemain,从pipeB读到done现在,当您关闭时done,它完全取决于 go 例程看到它的顺序。如果main是第一个看到它done已关闭,它将打破 for 循环并停止消耗pipeB。但是如果pipeB仍然尝试写入输出通道(ret <- tmp),它将在那里阻塞;所以它永远不会到达该<- done部分。有两个选项可以解决此问题:只在你的生成器中监听done,并让其他 go 例程使用for n := range in { }.select将您的发送逻辑也放入 a 中,以便您的生成器和管道可以检测何时done关闭。或者,您可能希望使用缓冲输出通道,但即使如此,此问题仍然可能发生。
打开App,查看更多内容
随时随地看视频慕课网APP