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

刘胖孩
刘胖孩
我们可以看到这是一个没有缓存能力的管道,然后往里面写666,然后就去管道里面读。这样肯定会出现问题啊!一个无缓存能力的管道,没有人读,你也写不了,没有人写,你也读不了,这正是一种死锁! 解决办法很简单,开辟两条协程,一条协程写,一条协程读。

第一种情形:无缓存能力的管道,自己写完自己读

go Copy
func main() {
	ch := make(chan int, 0)

	ch <- 666
	x := <- ch
	fmt.Println(x)
}

我们可以看到这是一个没有缓存能力的管道,然后往里面写666,然后就去管道里面读。这样肯定会出现问题啊!一个无缓存能力的管道,没有人读,你也写不了,没有人写,你也读不了,这正是一种死锁!

解决办法很简单,开辟两条协程,一条协程写,一条协程读。

第二种情形:协程来晚了

go Copy
func main() {
	ch := make(chan int,0)
	ch <- 666
	go func() {
		<- ch
	}()
}

我们可以看到,这条协程开辟在将数字写入到管道之后,因为没有人读,管道就不能写,然后写入管道的操作就一直阻塞。这时候你就有疑惑了,不是开辟了一条协程在读吗?但是那条协程开辟在写入管道之后,如果不能写入管道,就开辟不了协程。

第三种情形:管道读写时,相互要求对方先读/写

如果相互要求对方先读/写,自己再读/写,就会造成死锁。

go Copy
func main() {
	chHusband := make(chan int,0)
	chWife := make(chan int,0)

	go func() {
		select {
		case <- chHusband:
			chWife<-888
		}
	}()

	select {
		case <- chWife:
			chHusband <- 888
	}
}

第四种情形:读写锁相互阻塞,形成隐形死锁

go Copy
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()
	}
}

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

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

Update Time:Feb 04, 2026

Comments

Tips: Support some markdown syntax: **bold**, [bold](xxxxxxxxx), `code`, - list, > reference