带有sync.WaitGroup的Goroutines在最后一个wg.Done()之前结束

带有sync.WaitGroup的Goroutines在最后一个wg.Done()之前结束,go,goroutine,Go,Goroutine,我有一个示例代码(您可以在上找到): 我想我知道为什么会这样,但我解决不了。WaitGroup中有3个项目,我是指3个goroutine,第4个groutine使用通道中的数据。当最后一个groutine说wg.Done()时,程序结束了,因为wg.Wait()说每个goroutine都完成了,最后一个goroutine结果第四个goroutine不能使用,因为程序结束了。我尝试在第四个函数中使用wg.add(1)和wg.Done()添加plus one,但在本例中,我遇到了死锁 您生成的最后一

我有一个示例代码(您可以在上找到):


我想我知道为什么会这样,但我解决不了。WaitGroup中有3个项目,我是指3个goroutine,第4个groutine使用通道中的数据。当最后一个groutine说
wg.Done()
时,程序结束了,因为wg.Wait()说每个goroutine都完成了,最后一个goroutine结果第四个goroutine不能使用,因为程序结束了。我尝试在第四个函数中使用wg.add(1)和wg.Done()添加plus one,但在本例中,我遇到了死锁

您生成的最后一个要收集结果的goroutine没有被
main()
等待,因此
wg.Wait()
在那里返回,
main()
退出并重新获取剩余的goroutine。 据推测,当时只剩下一个收集goroutine,但它无法更新片段

还要注意的是,由于同样的原因,您的程序中存在数据竞争:当
main()
读取结果片段时,它不知道读取结果片段是否安全,也就是说,写入程序是否已完成写入

一个简单的解决方法是为该goroutine添加
wg.add(1)
,并在其中添加
defer wg.Done()

更好的解决方案是在
wg.Wait()

在从切片读取之前。这将使收集goroutine的
范围
循环终止,这还将在该goroutine和
main()之间创建一个适当的同步点
关闭通道是一种惯用的信号发送模式,如果关闭缓冲通道,消费者可以读取所有排队的数据,然后停止

此代码正常工作:

func main() {
    messages := make(chan int)
    var wg sync.WaitGroup
    var result []int

    // you can also add these one at
    // a time if you need to

    wg.Add(1)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 1
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 2
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 3
    }()

    // this goroutine added to signal end of data stream
    // by closing messages channel
    go func() {
        wg.Wait()
        close(messages)
    }()

    // if you need this to happen inside a go routine,
    // this channel is used for signalling end of the work,
    // also another sync.WaitGroup could be used, but for just one
    // goroutine, a single channel as a signal makes sense (there is no
    // groups)
    done := make(chan struct{})
    go func() {
        defer close(done)
        for i := range messages {
            fmt.Println(i)
            result = append(result, i)
        }
    }()

    <-done
    fmt.Println(result)
}
func main(){
消息:=make(chan int)
var wg sync.WaitGroup
var结果[]int
//您也可以在以下位置添加这些选项:
//如果你需要的话
工作组.添加(1)
go func(){
推迟工作组完成()
时间。睡眠(时间。秒*1)

在他们提到之前,科斯蒂克斯的回答是正确的

一个简单的解决方法是为该goroutine添加
wg.add(1)
,并在其中延迟
wg.Done()

这将导致您的循环在消息通道未关闭的情况下永远不会结束!因此,主goroutine将在您上次“收集”goroutine结束之前再次完成。您还将因goroutine绑定到您的
wg
WaitGroup而出错,因为该goroutine将永远不会发送
Done()
信号

然后当他们再次提到

更好的解决方案是在
wg.Wait()
之后和从切片读取之前关闭消息通道

他们建议的位置将再次给您带来相同的错误,因为您将等待同一个等待组
wg
。而您上次的“收集”goroutine将继续在您的
messages
频道中查找更多邮件,并且永远不会到达延迟的
wg.Done()

然后,Alex Yu的评论通过在阅读全部结果之前等待来修复它,这是一个很好的修复方法。但是如果您希望立即开始收集goroutine,而不是等待所有以前的goroutine(写入
消息
频道)为了在开始阅读上述频道之前完成,我建议如下

创建一个结果等待组,
Add(1)
在开始上一次“收集”goroutine之前,
在上一次“收集”goroutine内延迟wgfresult.Done()
,然后在最后,在
wg.Wait()
fmt.Println(result)
之间,您应该
关闭(消息)
wgfresult.Wait()

这使得你所有的围棋程序都能尽快开始,并且只在需要的时候在写作和阅读中等待

这里有一个建议解决方案的链接


例如,请参见-这与您的示例非常相似。我认为在这种情况下调用close是不够的。即使在close之后,读取频道的goroutine仍可能位于其循环体中,因此仍然能够修改片段。我想kostix建议进行此修改:。感谢您的回答。感谢您的帮助回答,基本上我也这么做了,接着是前面的评论。这部分是go func(){wg.Wait()close(messages)}()节省我的时间,谢谢!
2
1
[2 1]
func main() {
    messages := make(chan int)
    var wg sync.WaitGroup
    var result []int

    // you can also add these one at
    // a time if you need to

    wg.Add(1)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 1
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 2
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        time.Sleep(time.Second * 1)
        messages <- 3
    }()

    // this goroutine added to signal end of data stream
    // by closing messages channel
    go func() {
        wg.Wait()
        close(messages)
    }()

    // if you need this to happen inside a go routine,
    // this channel is used for signalling end of the work,
    // also another sync.WaitGroup could be used, but for just one
    // goroutine, a single channel as a signal makes sense (there is no
    // groups)
    done := make(chan struct{})
    go func() {
        defer close(done)
        for i := range messages {
            fmt.Println(i)
            result = append(result, i)
        }
    }()

    <-done
    fmt.Println(result)
}