写点什么

4 种 Golang 并发操作中常见的死锁情形

发布于: 2 小时前

​​摘要:什么是死锁,在 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() }}
复制代码


这两条协程,如果第一条协程先抢到了只写锁,另一条协程就不能抢只读锁了,那么因为另外一条协程没有读,所以第一条协程就写不进。


如果第二条协程先抢到了只读锁,另一条协程就不能抢只写锁了,那么因为另外一条协程没有写,所以第二条协程就读不到。


点击关注,第一时间了解华为云新鲜技术~ 

发布于: 2 小时前阅读数: 2
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
4种Golang并发操作中常见的死锁情形