我在使用以下代码时遇到了一些问题:
package main
import (
"fmt"
"sync"
)
// This program should go to 11, but sometimes it only prints 1 to 10.
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, wg) //
go func(){
for i := 1; i <= 11; i++ {
ch <- i
}
close(ch)
defer wg.Done()
}()
wg.Wait() //deadlock here
}
// Print prints all numbers sent on the channel.
// The function returns when the channel is closed.
func Print(ch <-chan int, wg sync.WaitGroup) {
for n := range ch { // reads from channel until it's closed
fmt.Println(n)
}
defer wg.Done()
}
我在指定的地方陷入僵局。我尝试设置wg.Add(1)
而不是2,它解决了我的问题。我的信念是,我没有成功地将频道作为Printer
函数的参数发送。有没有办法做到这一点?否则,我的问题的解决方案是将go Print(ch, wg)
行替换为:
go func() {
Print(ch)
defer wg.Done()
}
并将Printer
函数更改为:
func Print(ch <-chan int) {
for n := range ch { // reads from channel until it's closed
fmt.Println(n)
}
}
什么是最佳解决方案?
答案 0 :(得分:16)
好吧,首先您的实际错误是您为Print
方法提供了sync.WaitGroup
的副本,因此它不会调用您Done()
上的Wait()
方法重新package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, &wg)
go func() {
for i := 1; i <= 11; i++ {
ch <- i
}
close(ch)
defer wg.Done()
}()
wg.Wait() //deadlock here
}
func Print(ch <-chan int, wg *sync.WaitGroup) {
for n := range ch { // reads from channel until it's closed
fmt.Println(n)
}
defer wg.Done()
}
。
请改为尝试:
Print
现在,更改WaitGroup
方法以删除它的*.VC.db
通常是个好主意:该方法不需要知道有什么东西在等待它完成它的工作。
答案 1 :(得分:1)
我同意@ Elwinar的解决方案,即代码中的主要问题是将Waitgroup
的副本传递给Print
函数。
这意味着wg.Done()
是在wg
中定义的main
副本上运行的。因此,wg
中的main
无法降低,因此当你在wg.Wait()
中发生死锁时会发生死锁。
既然你也在询问最佳做法,我可以给你一些自己的建议:
请勿删除defer wg.Done()
中的Print
。由于你的main中的goroutine是发送者,而print是接收者,因此在接收器例程中删除wg.Done()
将导致未完成的接收者。这是因为只有您的发件人与您的主邮件同步,因此在您的发件人完成后,您的主邮件已完成,但接收器可能仍在工作。我的观点是:在你的主程序完成后不要留下一些悬垂的goroutines。关闭它们或等待它们。
记得要到处都做恐慌,特别是匿名的goroutine。我已经看到很多golang程序员忘记在goroutines中进行恐慌恢复,即使他们记得将恢复恢复到正常功能中。当您希望代码正常运行或至少在遇到意外情况时更优雅时,这一点至关重要。
在每次重要通话之前使用defer
,例如sync
相关电话,开头,因为您不知道代码可能会中断的位置。假设您在defer
之前删除了wg.Done()
,并且在您的示例中,您的匿名goroutine发生了恐慌。如果你没有恐慌恢复,它会恐慌。但是,如果你有恐慌恢复会发生什么?现在一切都很好吗?不会。由于恐慌导致wg.Wait()
被忽略,您将在wg.Done()
遇到僵局!但是,通过使用defer
,即使发生恐慌,此wg.Done()
也会在最后执行。此外,在close
之前推迟也很重要,因为其结果也会影响沟通。
所以这里是根据我上面提到的点修改的代码:
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go Print(ch, &wg)
go func() {
defer func() {
if r := recover(); r != nil {
println("panic:" + r.(string))
}
}()
defer func() {
wg.Done()
}()
for i := 1; i <= 11; i++ {
ch <- i
if i == 7 {
panic("ahaha")
}
}
println("sender done")
close(ch)
}()
wg.Wait()
}
func Print(ch <-chan int, wg *sync.WaitGroup) {
defer func() {
if r := recover(); r != nil {
println("panic:" + r.(string))
}
}()
defer wg.Done()
for n := range ch {
fmt.Println(n)
}
println("print done")
}
希望有所帮助:)