lock死锁

死锁的概念

在多线程编程中,我们为了防止多线程竞争共享资源而导致数据错乱,都会在操作共享资源之前加上互斥锁,只有成功获得到锁的线程,才能操作共享资源,获取不到锁的线程就只能等待,直到锁被释放。

死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。当线程处于这种僵持状态,而且无外力作用时,它们都将无法再向执行任务

死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。

例如,如果线程A获取了锁A,然后尝试对获取锁B,同时线程B已经获取了锁B,接着尝试获取锁A这时死锁就发生了。线程A永远得不到锁B,线程B也永远得不到锁A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。

死锁的原因

产生的原因可归结两点

  • 竞争不可剥夺资源
  • 推进顺序非法

竞争不可剥夺资源

竞争资源分为两种,一种是可剥夺资源,一种是不可剥夺资源。
可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程夺走,比如:CPU就属于可剥夺性资源;
不可剥夺资源,是指当系统把这类资源分配给某进程后,不能再强制收回,只能在进程将任务完成后、自行释放,比如:磁带机、打印机等。

推进顺序非法

若线程P1保持了资源R1,线程P2保持了资源R2,此时这两个进程再向前推进,
即,P1想要获取资源R2、P2想要获取资源R1,那么,P1将因资源R2已被P2占有而阻塞,P2将因资源R1已被P1占有而阻塞,此时便发生了进程死锁。

死锁同时满足四个条件才能发生

死锁只有同时满足以下四个条件才会发生:

  • 互斥条件;
  • 持有并等待条件;
  • 不可剥夺条件;
  • 环路等待条件;

互斥条件

互斥条件是指多个线程不能同时使用同一个资源
比如下图,如果线程 A 已经持有的资源,不能再同时被线程 B 持有,如果线程 B 请求获取线程 A 已经占用的资源,那线程 B 只能等待,直到线程 A 释放了资源。
image.png

持有并等待条件

持有并等待条件是指,当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1
image.png

不可剥夺条件

不可剥夺条件是指,当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。
image.png

环路等待条件

环路等待条件指都是,在死锁发生的时候,两个线程获取资源的顺序构成了环形链
比如,线程 A 已经持有资源 2,而想请求资源 1, 线程 B 已经获取了资源 1,而想请求资源 2,这就形成资源请求等待的环形图。
image.png

解决死锁问题

产生死锁的四个必要条件是:互斥条件、持有并等待条件、不可剥夺条件、环路等待条件。
那么避免死锁问题就只需要破环其中一个条件就可以。

预防死锁

  1. 资源一次性分配

一次性分配完所有的资源,这样就不会再有线程请求竞争资源了。(破坏了请求条件)

  1. 一个资源也不分配

就是,给一个线程分配资源时,如果无法给该线程分配它所需的所有资源,则直接,一个资源也不给该进程分配!(破坏请保持条件)

  1. 可剥夺资源

即当某线程获得了部分资源,但得不到其它资源,则自动释放自己已经占有的资源。(破坏不可剥夺条件)

常用的方式加锁超时,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行

  1. 资源有序分配法

让系统给每类资源都赋予一个编号,每一个线程按编号递增的顺序请求资源,释放则相反(破坏循环等待条件)

线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。

避免死锁

死锁避免:在使用前进行判断,只允许不会产生死锁的线程申请资源;
死锁避免是利用额外的检验信息,在分配资源时判断是否会出现死锁,只在不会出现死锁的情况下才分配资源。
两种避免办法:
1、如果一个线程的请求会导致死锁,则不启动该线程
2、如果一个线程的增加资源请求会导致死锁 ,则拒绝该申请。
其中最具有代表性的避免死锁算法是,银行家算法
** 银行家算法**简单描述就是:在避免死锁方法中允许线程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待。

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
死锁是指在多线程环境中,两个或多个线程因互相持有对方所需资源而无法继续执行的情况。在你的问题中,你提到了 `lock_guard`、`unique_lock` 和 `lock`,这些是 C++ 中用于实现线程同步和互斥的工具。 `lock_guard` 是 C++ 标准库提供的一个模板类,用于自动管理互斥量的加锁和解锁。比如可以这样使用它: ```cpp std::mutex mtx; { std::lock_guard<std::mutex> lock(mtx); // 加锁 // 执行需要同步的代码块 } // 自动解锁 ``` `unique_lock` 也是一个模板类,和 `lock_guard` 类似,用来管理互斥量的加锁和解锁。不同的是,`unique_lock` 提供了更多的灵活性和可定制性,可以在需要时手动加锁和解锁。使用方式如下: ```cpp std::mutex mtx; { std::unique_lock<std::mutex> lock(mtx); // 手动加锁 // 执行需要同步的代码块 lock.unlock(); // 手动解锁 } // 自动解锁 ``` `lock` 是一个函数模板,可以一次性对多个互斥量进行加锁,避免出现死锁。使用方式如下: ```cpp std::mutex mtx1, mtx2; std::lock(mtx1, mtx2); // 原子地对 mtx1 和 mtx2 加锁 // 执行需要同步的代码块 mtx1.unlock(); mtx2.unlock(); // 解锁 ``` 然而,死锁往往是由于线程之间的加锁顺序导致的。当两个或多个线程持有某些资源并试图获取对方持有的资源时,就可能出现死锁。为了避免死锁,你需要仔细规划线程之间的加锁顺序,并确保在持有资源时不会阻塞其他线程的执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值