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
浙公网安备 33010602011771号