常见导致通道死锁的场景:
- 发送端无接收方:当一个协程向通道发送数据,但没有其他协程从该通道接收数据时,会发生死锁。例如:
package main
func main() {
ch := make(chan int)
ch <- 1 // 这里发送数据,但没有接收方,会导致死锁
}
- 接收端无发送方:当一个协程尝试从通道接收数据,但没有其他协程向该通道发送数据时,会发生死锁。例如:
package main
func main() {
ch := make(chan int)
<-ch // 这里接收数据,但没有发送方,会导致死锁
}
- 双向阻塞:在两个协程之间通过通道进行通信时,如果双方都在等待对方先操作,会导致死锁。例如:
package main
import (
"fmt"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
ch1 <- 1
<-ch2
}()
go func() {
ch2 <- 2
<-ch1
}()
}
避免死锁的代码设计方法:
- 确保有接收方:在发送数据之前,确保有协程准备好从通道接收数据。例如:
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
data := <-ch
fmt.Println("Received:", data)
}()
ch <- 1
}
- 确保有发送方:在接收数据之前,确保有协程准备好向通道发送数据。例如:
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
data := <-ch
fmt.Println("Received:", data)
}
- 避免双向阻塞:合理设计协程间的通信逻辑,避免双方互相等待。例如,通过引入一个额外的信号通道来打破死锁:
package main
import (
"fmt"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
signal := make(chan struct{})
go func() {
<-signal
ch1 <- 1
<-ch2
}()
go func() {
ch2 <- 2
<-ch1
close(signal)
}()
close(signal)
}