英文:
WaitGroup - fatal error: all goroutines are asleep - deadlock
问题
有人能解释一下为什么这段代码会抛出一个"fatal error: all goroutines are asleep - deadlock!"的错误吗?
我找不到问题出在哪里。我看到一些关于这个特定错误的问题,但原因大多是在没有关闭通道的情况下循环遍历通道。
谢谢!
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go wait(v, ch, wg)
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration, wg sync.WaitGroup) {
defer wg.Done()
time.Sleep(seconds * time.Second)
c <- seconds
}
这段代码中的问题在于wait
函数的参数wg sync.WaitGroup
是按值传递的,而不是按引用传递。这意味着在wait
函数中对wg
的修改不会影响到main
函数中的wg
。因此,wg.Wait()
在等待的时候,wait
函数中的wg.Done()
没有被调用,导致所有的goroutine都无法退出,最终引发了死锁错误。
要解决这个问题,你可以将wg sync.WaitGroup
作为指针传递给wait
函数,这样wait
函数中对wg
的修改就会影响到main
函数中的wg
。修改后的代码如下:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go wait(v, ch, &wg)
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(seconds * time.Second)
c <- seconds
}
这样修改后,代码就不会再抛出死锁错误了。
英文:
Can someone explain why this code throws an "fatal error: all goroutines are asleep - deadlock!"?
I can't seem to find what is wrong. I've seen some questions about this specific error, but the reason was mostly looping through a channel without closing it.
Thank you!
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go wait(v, ch, wg)
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration, wg sync.WaitGroup) {
defer wg.Done()
time.Sleep(seconds * time.Second)
c <- seconds
}
答案1
得分: 4
你需要通过引用而不是值传递WaitGroup
。否则,Done
方法将没有效果。该类型的文档中有如下说明:
> 在首次使用后,WaitGroup不能被复制。
将你的代码修复如下,它将正常工作:
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go wait(v, ch, &wg)
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(seconds * time.Second)
c <- seconds
}
此外,还可以将此模式表达如下:
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go func() {
defer wg.Done()
wait(v, ch)
}()
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration) {
time.Sleep(seconds * time.Second)
c <- seconds
}
这种情况下的好处是,wait
函数不需要意识到任何等待组(它可以是第三方函数),并且不会对传递等待组的方式(值传递还是引用传递)产生混淆。
英文:
You have to pass the WaitGroup
by reference, not by value. Otherwise the Done
has no effect. The documentation of this type says:
> A WaitGroup must not be copied after first use.
Fix your code to this and it will work:
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go wait(v, ch, &wg)
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(seconds * time.Second)
c <- seconds
}
It is also common to express this pattern as follows:
func main() {
ch := make(chan time.Duration)
var wg sync.WaitGroup
for _, v := range []time.Duration{5, 1} {
wg.Add(1)
go func() {
defer wg.Done()
wait(v, ch)
}()
fmt.Println(<-ch)
}
wg.Wait()
}
func wait(seconds time.Duration, c chan time.Duration) {
time.Sleep(seconds * time.Second)
c <- seconds
}
What's nice about this case is that wait
doesn't have to be aware of any wait groups (it could be a 3rd-party function, for example), and there's no confusion about passing a wait group by value or reference.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论