什么叫死锁
死锁(deadlock),指的是多线程(多进程)在运行中被阻塞的情况。如下图所示,进程1拥有资源1并等待资源2,进程2拥有资源2等待资源1。进程1需要等待资源2才会释放资源1,进程2需要等待资源1才会释放资源2,导致两个进程相互阻塞。死锁就好比两个人面对面碰上对方,两人互不相让,都在等待对方先让路。
C++代码死锁例子
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mutex;
void func() {
std::cout << "func try to lock mutex" << std::endl;
std::lock_guard<std::mutex> locker(mutex); // thread线程申请锁资源, 等待锁释放
std::cout << "func try to lock mutex success" << std::endl;
}
int main() {
std::cout << "main try to lock mutex" << std::endl;
std::lock_guard<std::mutex> locker(mutex); // 主线程占用mutex
std::thread thread(func);
thread.join();
std::cout << "main try to lock mutex success" << std::endl;
return 0;
}
上述代码申请了共享mutex资源,在主线程先申请锁资源并上锁。运行至thread线程时,也申请所资源,并等待锁释放。主线程在等待thread线程结束才会释放锁资源,thread线程也在等主线程释放mutex资源才会继续运行,所以程序处于死锁状态。输出如下:
main try to lock mutex
func try to lock mutex
"func try to lock mutex success"和"main try to lock mutex success"均无法输出。
产生死锁的必要条件
当同时满足以下四个条件就会引发死锁:
互斥: 资源在同一时间只能被一个线程(进程)占用,无法在多个线程(进程)之间共享。
保持和等待: 一个线程(进程)至少占用一个资源并且等待被其他线程(进程)占用的资源。
无抢占: 资源只能等待某个线程(进程)释放才能被其他线程(进程)。
循环等待: 一系列线程(进程)以循环的形式相互等待资源的释放。
如何预防死锁
从产生死锁的四个必要条件为方向,只要不满足其中一条就可以避免死锁。
避免互斥: 不申请共享资源,所有资源申请为独占资源。
避免保持和等待: 保证线程(进程)请求资源时,该资源不被其他资源占有。要求线程(进程)在开始执行前就请求并分配所有资源,或者允许线程(进程)在没有资源时才能申请资源。这样的缺点就是资源利用率低,资源申请都是饿汉模式。
避免无抢占:
- 如果一个占用很多资源的线程(进程)在请求其他资源,并且请求的资源不能马上分配给该线程(进程)时,该线程(进程)占用的所有资源都被释放。
- 建立资源等待列表,将线程(进程)的被抢占资源添加到等待列表中。
- 当线程(进程)重新获取旧资源以及正在请求的新资源时,线程(进程)重新启动。
避免循环等待: 强制所有资源的申请顺序,并要求所有线程(进程)以一定顺序请求资源。