死锁概念
所谓死锁,是指两个或者两个以上线程在执行过程中,因争夺资源而产生互相等待的现象,若无外力作用,他们都将无法推进下去,此时,称系统处于死锁。
死锁产生条件
- 互斥条件:进程对所分配的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时,还有其它的进程请求该资源,则请求者只能等待。
- 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,但是该资源正在被其他进程占有,此时,请求进程阻塞,但又对自己所获得的资源保持不放。
- 不剥夺条件:指进程已经获得的资源,在没有使用完之前,不能被剥夺,只能由该进程使用完后自己释放。
- 环路等待条件:指在发生死锁时,必然存在一个进程资源的环形链,即进程集合{p0、p1、p2、p3、…pn}中的p0正在等待一个p1占用的资源,pn正在等待一个p0占用的资源。
死锁解决方法
- 死锁预防:破坏导致死锁必要条件中的任何一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件。将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了 环路等待条件。
- 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全。例如,使用银行家算法。
- 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
- 死锁解除:与死锁检测结合使用。它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。
例子
package main
import (
"fmt"
"strings"
)
// 做整个功能模块的封装
type LogProcess struct {
path string // 文件的路径
influxDBsn string // 存储数据库的信息
rc chan string // 从读取模块到
wc chan string
}
// 用引用去接受 不会做拷贝的操作 并且能够改变自己的属性
func (l *LogProcess) ReadFromFile() {
// 读取模块
line := "message"
l.rc <- line
}
func (l *LogProcess) Process() {
// 解析模块
data := <-l.rc
l.wc <- strings.ToUpper(data)
}
func (l *LogProcess) WriteToInfluxDB() {
// 写入模块
fmt.Println(<-l.wc)
}
func main() {
lp := &LogProcess{
path: "/tmp/access.log",
influxDBsn: "username&password",
wc: make(chan string),
rc: make(chan string),
}
// 三个模块独立的并发执行
// 怎么实现三个模块之间的通信 -> 用chan
go lp.ReadFromFile()
go lp.Process()
go lp.WriteToInfluxDB()
select {}
}
输出结果
MESSAGE fatal error: all goroutines are asleep - deadlock!
分析
从结果上看程序正常执行完成之后,产生了死锁,报错信息也很明显,所有的goroutines都在睡眠状态,都在等待数据的产生,所以出现死锁。
参考:https://blog.csdn.net/baidu_33604078/article/details/78156152