以下是整理出的5个典型golang死锁场景:
1、主线程在通道写入之前,先行读取
func main() {
ch := make(chan int[, n])
fmt.Println(<-ch)
go func() {
ch <- 1
}()
}
或
func main() {
ch := make(chan int[, n])
fmt.Println(<-ch)
ch <- 1
}
2、主线程对无缓冲通道(或缓冲已满),先行写入
func main() {
ch := make(chan int)
ch <- 1
go func() {
fmt.Println(<-ch)
}()
}
或
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
3、主线程内,读取通道次数超过协程中写入次数
func main() {
ch := make(chan int[, n])
go func() {
ch <- 1
}()
//多次读取
fmt.Println(<-ch, <-ch)
//或轮询监听
for v := range ch {
fmt.Println(v)
}
}
4、主线程内,对多个通道挨个读取的顺序,与协程中写入的不一致(除非通道有足够的缓冲)
—— 协程中写入下一个通道时,因上一个通道堵塞而被锁,造成主线程读取一个空通道而死锁
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
ch1 <- 1
ch2 <- 2
}()
fmt.Println(<-ch2) //这里读取的顺序颠倒
fmt.Println(<-ch1)
}
5、通道1中调用了通道2,通道2中调用通道1
—— 2个通道相互调用,监听读取的永远都是空通道,造成主线程死锁等待
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
select {
case <-ch1:
ch2 <- 20
}
}()
select {
case <-ch2:
ch1 <- 10
}
}
总结:
1、golang死锁全部发生在主线程
2、死锁的原因无外乎以下2种情况:
—— 主线程读取一个空的,且没有被close的通道
—— 通道缓冲不足时,主线程向没有协程消费的通道写入