什么是死锁?
死锁并不是锁的一种,而是一种错误使用锁导致的现象,死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。系统发生死锁现象不仅浪费大量的系统资源,甚至导致整个系统崩溃,带来灾难性后果。所以,对于死锁问题在理论上和技术上都必须予以高度重视。
造成死锁的常见情况
- 单go程自己死锁
func main() {
ch:=make(chan int) //声明
//向ch中写入数据,ch此时等待读出数据,造成堵塞,使得下方读出数据部分的代码不执行,造成死锁
ch <- 89
num:=<- ch
fmt.Println(num)
}
2.go程间channel访问顺序导致死锁
func main() {
ch:=make(chan int)
//进行数据的读取,但是无数据读取,使得阻塞,导致下面go程中的代码不执行造成死锁
num:= <- ch
fmt.Println(num)
//在go程向ch写入数据
go func() {
ch <- 89
}()
}
3.多go程,多channel 交叉死锁
func main() {
ch1:=make(chan int)
ch2:=make(chan int)
//此时主go程和子go程,互相掌握着各自的条件,但都无法释放,造成死锁
go func() {
for {
select {
case num:=<-ch1:
ch2 <- num
}
}
}()
for {
select {
case num := <-ch2:
ch1<-num
}
}
}
- 将 互斥锁、读写锁 与 channel 混用。 —— 隐性死锁。
下面代码造成隐形死锁的原因是,进行readGo函数时,以读的模式加锁,同时 channel 等待数据写入,以便于数据的读出,此时channel等到数据写入造成堵塞,下面代码无法执行,造成以读模式的锁无法解锁。但进入到writeGo函数中时,向下执行时,遇到以写模式加锁时会造成堵塞,因为上方以读的模式的锁并没有解锁,造成以写模式加锁下方的代码无法执行,无法向channel中写入数据,使其互相堵塞,但是控制台并不报错,该死锁称为隐性死锁。
import (
"fmt"
"math/rand"
"sync"
"time"
)
var rwMutex sync.RWMutex
//读时共享,写时独占
func readGo(in <- chan int,idx int) {
for {
rwMutex.RLock() //以读模式加锁
num:=<-in
fmt.Println("----%dth 读go程,读出:%d\n",idx,num)
rwMutex.RUnlock() //以读模式解锁
}
}
func writeGo(out chan <- int,idx int) {
for {
//生成随机数
num := rand.Intn(1000)
rwMutex.Lock() //以写模式加锁
out <- num
fmt.Println("%dth 写Go程,写入:%d\n",idx,num)
time.Sleep(time.Millisecond * 300)
rwMutex.Unlock() //以写模式解锁
}
}
func main() {
//播种随机数种子
rand.Seed(time.Now().UnixNano())
ch:=make(chan int) //用于 数据传递的channel
for i:=0;i<5;i++ {
go readGo(ch,i+1)
}
for i:=0;i<5;i++ {
go writeGo(ch,i+1)
}
}