共计 1296 个字符,预计需要花费 4 分钟才能阅读完成。
摘要: 什么是死锁,在 Go 的协程外面死锁通常就是永恒阻塞了,你拿着我的货色,要我先给你而后再给我,我拿着你的货色又让你先给我,不然就不给你。我俩都这么想,这事就解决不了了。
本文分享自华为云社区《Golang 并发操作中常见的死锁情景》,作者:Regan Yue。
什么是死锁,在 Go 的协程外面死锁通常就是永恒阻塞了,你拿着我的货色,要我先给你而后再给我,我拿着你的货色又让你先给我,不然就不给你。我俩都这么想,这事就解决不了了。
第一种情景:无缓存能力的管道,本人写完本人读
先上代码:
func main() {ch := make(chan int, 0)
ch <- 666
x := <- ch
fmt.Println(x)
}
咱们能够看到这是一个没有缓存能力的管道,而后往里面写 666,而后就去管道外面读。这样必定会呈现问题啊!一个无缓存能力的管道,没有人读,你也写不了,没有人写,你也读不了,这正是一种死锁!
fatal error: all goroutines are asleep - deadlock!
解决办法很简略,开拓两条协程,一条协程写,一条协程读。
第二种情景:协程来晚了
func main() {ch := make(chan int,0)
ch <- 666
go func() {<- ch}()}
咱们能够看到,这条协程开拓在将数字写入到管道之后,因为没有人读,管道就不能写,而后写入管道的操作就始终阻塞。这时候你就有纳闷了,不是开拓了一条协程在读吗?然而那条协程开拓在写入管道之后,如果不能写入管道,就开拓不了协程。
第三种情景:管道读写时,互相要求对方先读 / 写
如果互相要求对方先读 / 写,本人再读 / 写,就会造成死锁。
func main() {chHusband := make(chan int,0)
chWife := make(chan int,0)
go func() {
select {
case <- chHusband:
chWife<-888
}
}()
select {
case <- chWife:
chHusband <- 888
}
}
先来看看老婆协程,chWife 只有能读出来,也就是老婆有钱,就给老公发个八百八十八的大红包。
再看看老公的协程,一看不得了,咋啦?老公也说只有他有钱就给老婆包个八百八十八的大红包。
两个人都说本人没钱,老公也给老婆发不了红包,老婆也给老公发不了红包,这就是死锁!
第四种情景:读写锁互相阻塞,造成隐形死锁
先来看一看代码:
func main() {
var rmw09 sync.RWMutex
ch := make(chan int,0)
go func() {rmw09.Lock()
ch <- 123
rmw09.Unlock()}()
go func() {rmw09.RLock()
x := <- ch
fmt.Println("读到",x)
rmw09.RUnlock()}()
for {runtime.GC()
}
}
这两条协程,如果第一条协程先抢到了只写锁,另一条协程就不能抢只读锁了,那么因为另外一条协程没有读,所以第一条协程就写不进。
如果第二条协程先抢到了只读锁,另一条协程就不能抢只写锁了,那么因为另外一条协程没有写,所以第二条协程就读不到。
点击关注,第一工夫理解华为云陈腐技术~