go并发: 死锁与阻塞
一,空select语句:会报死锁
代码:
// 入口函数
func main() {
select{}
}
运行结果:
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
/data/goapp/imagebank/main.go:8 +0xf
exit status 2
二,sync.WaitGroup,协程数量不足任务数量时,会报死锁
代码:
// 入口函数
func main() {
var wg sync.WaitGroup
// 假设有两个任务要执行
wg.Add(2)
go func() {
defer wg.Done() // 完成一个任务
fmt.Println("任务1完成")
}()
/*
go func() {
defer wg.Done() // 完成一个任务
fmt.Println("任务2完成")
}()
*/
// 阻塞,直到上面两个 goroutine 执行完
wg.Wait()
fmt.Println("所有任务完成")
}
我们注释掉了两个协程中的一个,
运行结果:
$ go run main.go
任务1完成
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000140b0?)
/usr/local/soft/go/src/runtime/sema.go:71 +0x25
sync.(*WaitGroup).Wait(0xc000044740?)
/usr/local/soft/go/src/sync/waitgroup.go:118 +0x48
main.main()
/data/goapp/imagebank/main.go:30 +0x73
exit status 2
三,主协程内读写无缓冲channel会报死锁
1,代码:
// 入口函数
func main() {
ch := make(chan int, 0)
ch <- 666
x := <- ch
fmt.Println(x)
}
说明:
当 Go 协程给一个无缓冲channel发送数据时,需要有其他 Go 协程来接收数据。
如果没有的话,程序就会在运行时触发 panic,形成死锁
运行结果:
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/data/goapp/imagebank/main.go:17 +0x36
exit status 2
2, 解决方案一:
增加缓冲即可:
// 入口函数
func main() {
ch := make(chan int, 1)
ch <- 666
x := <- ch
fmt.Println(x)
}
3,解决方案二:用select判断超时退出
// 入口函数
func main() {
ch := make(chan int)
select {
case data := <-ch:
fmt.Println("Received:", data)
case <-time.After(2 * time.Second): // 等待 2 秒超时
fmt.Println("Timeout, no data received")
}
}
运行结果:
$ go run main.go
Timeout, no data received
四,无缓冲channel,协程启动在写入之后,会报死锁
代码:
// 入口函数
func main() {
ch := make(chan int,0)
ch <- 666
go func() {
x := <- ch
fmt.Println(x)
}()
}
原因:协程启动是在写入666之后,写入时还没有协程接收数据,
所以会报错
运行结果:
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/data/goapp/imagebank/main.go:17 +0x2d
exit status 2
解决: 先启动接收的协程,再写入到channel即可
// 入口函数
func main() {
ch := make(chan int,0)
go func() {
x := <- ch
fmt.Println(x)
}()
ch <- 666
}
再次运行:
$ go run main.go
666
五,有缓冲通道:无数据时读取,会报死锁:
代码:
// 入口函数
func main() {
ch := make(chan int, 1)
x := <- ch
fmt.Println(x)
}
运行结果:
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/data/goapp/imagebank/main.go:13 +0x32
exit status 2
六,有缓冲channnel的缓存已经写满,仍然向通道写数据,但没有协程读取,会报死锁
代码:
// 入口函数
func main() {
ch := make(chan int, 1)
ch <- 666 //full
ch <- 888 //panic
fmt.Println("write finish")
}
运行结果:
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/data/goapp/imagebank/main.go:12 +0x45
exit status 2
七,向关闭的channel写入,会引发panic
// 入口函数
func main() {
ch := make(chan int, 1)
close(ch)
ch <- 1 // 向已关闭的 channel 写入数据会引发 panic
}
运行结果:
$ go run main.go
panic: send on closed channel
goroutine 1 [running]:
main.main()
/data/goapp/imagebank/main.go:10 +0x3a
exit status 2
八,从关闭的channel读取数据:读取完后会返回0
channel关闭后不允许再写入,但仍然可以读取
例子:
// 入口函数
func main() {
ch := make(chan int, 1)
ch <- 42
close(ch)
value, ok := <-ch
fmt.Println(value, ok) // 输出: 42 true
value, ok = <-ch
fmt.Println(value, ok) // 输出: 0 false
}
运行结果:
$ go run main.go
42 true
0 false
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2021-03-01 spring boot单元测试之二:用MockMvc为controller做单元测试(spring boot 2.4.3)
2020-03-01 docker的常用操作之二:docker内无法解析dns之firewalld设置等
2020-03-01 centos8安装fastdfs6.06集群方式三之:storage的安装/配置/运行
2020-03-01 centos8安装fastdfs6.06集群方式二之:tracker的安装/配置/运行
2020-03-01 centos8安装fastdfs6.06集群方式一之:软件下载与安装
2020-03-01 fastdfs之同一台storage server下包含多个store path
2020-03-01 centos8安装fastdfs6.06(单机方式)