带有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)
}