1. 无缓冲channel收发导致卡住场景
- 场景:当一个Goroutine在无缓冲channel上发送数据,而没有其他Goroutine同时在该channel上接收数据时,发送操作会阻塞。同理,当一个Goroutine在无缓冲channel上接收数据,而没有其他Goroutine同时在该channel上发送数据时,接收操作也会阻塞。例如:
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 1 // 这里会阻塞,因为没有其他Goroutine接收
}()
// 主Goroutine没有接收操作,程序会卡住
}
- 解决方案:确保在发送前有接收方准备好,或者在接收前有发送方准备好。可以通过调整Goroutine的启动顺序或使用同步机制来保证。例如:
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
data := <-ch // 先准备好接收
fmt.Println("Received:", data)
}()
ch <- 1 // 发送数据,此时接收方已准备好
}
- 设计阶段避免:在设计时明确数据的流动方向,合理规划Goroutine和channel的使用。对于无缓冲channel,确保发送和接收操作在逻辑上紧密相关且几乎同时进行。
2. 缓冲channel满时发送导致卡住场景
- 场景:当缓冲channel已满,继续向其发送数据时,发送操作会阻塞。例如:
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3 // 这里会阻塞,因为channel缓冲区大小为2,已填满
}
- 解决方案:增加接收操作以腾空缓冲区,或者使用
select
语句结合default
分支来避免阻塞。例如使用select
:
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
select {
case ch <- 3:
fmt.Println("Sent 3 successfully")
default:
fmt.Println("Channel is full, cannot send 3")
}
}
- 设计阶段避免:合理预估数据的生产和消费速度,设置合适大小的缓冲channel。同时,在设计时考虑到可能出现的缓冲区满的情况,提前规划好处理逻辑。
3. 缓冲channel空时接收导致卡住场景
- 场景:当缓冲channel为空,继续从其接收数据时,接收操作会阻塞。例如:
package main
import "fmt"
func main() {
ch := make(chan int, 2)
data := <-ch // 这里会阻塞,因为channel为空
fmt.Println("Received:", data)
}
- 解决方案:先发送数据到channel,或者使用
select
语句结合default
分支来避免阻塞。例如:
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2)
select {
case data := <-ch:
fmt.Println("Received:", data)
default:
fmt.Println("Channel is empty, cannot receive")
}
}
- 设计阶段避免:同样要合理规划数据的生产和消费逻辑,确保在接收数据前有数据发送到channel。如果可能出现空接收的情况,提前设计好应对策略,如使用
select
结合default
。