我正在尝试同步 2 个 goroutine,例如 gr1 和 gr2 交替运行。 为此,我使用 2 个通道来协调。
当我使用
time.Sleep
等待 goroutine 完成时,它工作正常,没有任何错误。
当我使用
sync.Waitgroup
等待 goroutine 时,它会引发死锁错误。
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x14000002101?)
/usr/local/go/src/runtime/sema.go:62 +0x2c
sync.(*WaitGroup).Wait(0x1027bd710)
/usr/local/go/src/sync/waitgroup.go:116 +0x74
package main
import (
"fmt"
"time"
)
func main() {
ch1, ch2 := make(chan int, 1), make(chan int, 1)
go f1(ch1, ch2)
go f2(ch2, ch1)
ch1 <- 1
time.Sleep(time.Second * 1)
}
func f1(ch1, ch2 chan int) {
for i := 1; i < 5; i++ {
num := <-ch1
fmt.Println("f1", num*i)
ch2 <- num * i
}
}
func f2(ch2, ch1 chan int) {
for i := 1; i < 5; i++ {
num := <-ch2
fmt.Println("f2", num*i)
ch1 <- num * i
}
}
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
ch1, ch2 := make(chan int), make(chan int)
wg = sync.WaitGroup{}
wg.Add(2)
go f1(ch1, ch2)
go f2(ch2, ch1)
ch1 <- 1
wg.Wait()
}
func f1(ch1, ch2 chan int) {
for i := 1; i < 5; i++ {
num := <-ch1
fmt.Println("f1", num*i)
ch2 <- num * i
}
wg.Done()
}
func f2(ch2, ch1 chan int) {
for i := 1; i < 5; i++ {
num := <-ch2
fmt.Println("f2", num*i)
ch1 <- num * i
}
wg.Done()
}
对于缓冲通道,不会有错误。
我想了解 waitgroup 所做的与 time.sleep 不同的事情。
谢谢
在 waitgroup 示例中,您在
f2
func 和最后一次迭代中 ch1 <- num * i
尝试写入 ch1,而该通道没有可用的读取器,并且因为它是无缓冲通道:
来自无缓冲通道的接收在该通道上相应的发送完成之前进行同步。 (https://go.dev/ref/mem#chan)
由于
f2
停在那条线上,它永远不会到达 wg.Done()
并且 wg.Wait
会引发死锁恐慌。
更改 f2 可以解决问题,同时还有更好的选择:
func f2(ch2, ch1 chan int) {
for i := 1; i < 5; i++ {
num := <-ch2
fmt.Println("f2", num*i)
if i < 4 {
ch1 <- num * i
}
}
wg.Done()
}