Go 未知长度的闭合通道

Go 未知长度的闭合通道,go,concurrency,channel,Go,Concurrency,Channel,我无法在不知道频道的 长度 这是合乎逻辑的-第一个goroutine在第二个goroutine试图发送到它时关闭通道。在这种情况下,关闭渠道的最佳方法是什么 一旦频道关闭,您就不能在其上发送更多的值,否则它会恐慌。这就是你的经历 这是因为您启动了使用同一通道的多个goroutine,并且它们在该通道上发送值。然后你关闭每个频道。而且由于它们没有同步,一旦第一个goroutine到达它关闭它的点,其他人可能(而且他们将)仍然会继续在上面发送值:panic 您只能关闭该频道一次(尝试关闭已关闭的频道

我无法在不知道频道的
长度


这是合乎逻辑的-第一个goroutine在第二个goroutine试图发送到它时关闭通道。在这种情况下,关闭渠道的最佳方法是什么

一旦频道关闭,您就不能在其上发送更多的值,否则它会恐慌。这就是你的经历

这是因为您启动了使用同一通道的多个goroutine,并且它们在该通道上发送值。然后你关闭每个频道。而且由于它们没有同步,一旦第一个goroutine到达它关闭它的点,其他人可能(而且他们将)仍然会继续在上面发送值:panic

您只能关闭该频道一次(尝试关闭已关闭的频道也会导致恐慌)。当所有在上面发送值的goroutine都完成时,您应该这样做。为了做到这一点,您需要检测所有发送方goroutine何时完成。检测这种情况的惯用方法是使用

对于每个已启动的发送方goroutine,我们使用向
WaitGroup
添加1。发送值的每个goroutine都可以通过调用。最好以延迟语句的形式执行此操作,因此如果您的goroutine突然终止(例如恐慌),
WaitGroup.Done()
仍将被调用,并且不会让其他goroutine挂起(等待赦免-一个“丢失的”
WaitGroup.Done()
调用永远不会出现…)

并将等待,直到所有发送方goroutine完成,并且只有在此之后,并且只有一次,它才会关闭通道。我们想检测这个“全局”完成事件,并在处理发送给它的值的过程中关闭通道,所以我们必须在它自己的goroutine中这样做

接收器goroutine将一直运行,直到通道关闭,因为我们将
用于。。。范围
在频道上构建。由于它在主goroutine中运行,程序将不会退出,直到所有值都从通道正确接收和处理。用于。。。范围构造循环,直到接收到通道关闭前发送的所有值

请注意,下面的解决方案也适用于缓冲和非缓冲通道,无需修改(尝试使用带有
ch:=make(chan int,100)
的缓冲通道)

正确的解决方案(在上尝试):

func gen(ch chan int,wg*sync.WaitGroup){
推迟工作组完成()
变量i int
为了{
时间。睡眠(时间。毫秒*10)
第100章{
打破
}
}
}
func接收器(ch chan int){
对于i:=范围ch{
fmt.Println(“收到:”,i)
}
}
func main(){
ch:=制造(成交量)
wg:=&sync.WaitGroup{}
对于i:=0;i<10;i++{
工作组.添加(1)
总干事(总干事、工作组)
}
go func(){
wg.Wait()
关闭(ch)
}()
接收器(ch)
}
注意:

请注意,
receiver(ch)
在主goroutine中运行,等待
WaitGroup
并在其自身(非主)goroutine中关闭通道的代码非常重要;而不是相反。如果您切换这2个,可能会导致“提前退出”,即并非所有值都可以从通道接收和处理。这是因为当主goroutine完成时,Go程序退出(spec:)。它不会等待其他(非主)goroutine完成。因此,如果等待和关闭通道将在主goroutine中,在关闭通道后,程序可以随时退出,而不是等待另一个goroutine,在这种情况下,该goroutine将循环接收来自通道的值。

使用Go频道的一个一般原则是,不要从接收方关闭频道,如果频道有多个并发发送方,也不要关闭频道。

一旦标记为清理,每个通道最终都将进行GCD,因此可以保持通道未关闭。唯一的区别是,在几个周期后,如果不明确关闭,该通道将可用于
gc

尽管如此,如果你能关闭这个频道总是好的。请通过以下链接进行详细解释


文章和演示了在1:N、N:1或M:N(发送者:接收者)情况下关闭频道的各种方法。

感谢您的回答我根据您的想法实施了我的解决方案。
package main

import (
    "fmt"
    "time"
)

func gen(ch chan int) {
    var i int
    for {
        time.Sleep(time.Millisecond * 10)
        ch <- i
        i++
        // when no more data (e.g. from db, or event stream)
        if i > 100 {
            break
        }
    }

    // hot to close it properly?
    close(ch)
}

func receiver(ch chan int) {
    for i := range ch {
        fmt.Println("received:", i)
    }
}

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

    for i := 0; i < 10; i++ {
        go gen(ch)
    }

    receiver(ch)
}
panic: send on closed channel

goroutine 8 [running]:
main.gen(0xc82001a0c0)
    /home/exu/src/github.com/exu/go-workshops/100-concurrency-channels/16-close-problem.go:12 +0x57
created by main.main
    /home/exu/src/github.com/exu/go-workshops/100-concurrency-channels/16-close-problem.go:35 +0xbd

goroutine 1 [panicwait]:
runtime.gopark(0x0, 0x0, 0x50b8e0, 0x9, 0x10, 0x1)
    /usr/lib/go/src/runtime/proc.go:185 +0x163
runtime.main()
    /usr/lib/go/src/runtime/proc.go:121 +0x2f4
runtime.goexit()
    /usr/lib/go/src/runtime/asm_amd64.s:1696 +0x1

goroutine 6 [sleep]:
time.Sleep(0x989680)
    /usr/lib/go/src/runtime/time.go:59 +0xf9
main.gen(0xc82001a0c0)
    /home/exu/src/github.com/exu/go-workshops/100-concurrency-channels/16-close-problem.go:11 +0x29
created by main.main
    /home/exu/src/github.com/exu/go-workshops/100-concurrency-channels/16-close-problem.go:33 +0x79

goroutine 7 [sleep]:
time.Sleep(0x989680)
    /usr/lib/go/src/runtime/time.go:59 +0xf9
main.gen(0xc82001a0c0)
    /home/exu/src/github.com/exu/go-workshops/100-concurrency-channels/16-close-problem.go:11 +0x29
created by main.main
    /home/exu/src/github.com/exu/go-workshops/100-concurrency-channels/16-close-problem.go:34 +0x9b
exit status 2
func gen(ch chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    var i int
    for {
        time.Sleep(time.Millisecond * 10)
        ch <- i
        i++
        // when no more data (e.g. from db, or event stream)
        if i > 100 {
            break
        }
    }
}

func receiver(ch chan int) {
    for i := range ch {
        fmt.Println("received:", i)
    }
}

func main() {
    ch := make(chan int)
    wg := &sync.WaitGroup{}

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go gen(ch, wg)
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    receiver(ch)
}