欢迎光临散文网 会员登陆 & 注册

死锁

2023-07-02 17:10 作者:追逐彗星的尾巴  | 我要投稿

死锁(Deadlock)是指在并发系统中,两个或多个进程(线程)互相持有对方所需资源而无法继续执行的状态。这种情况下,进程将永远等待下去,直到外部干预才能解除死锁。

死锁通常涉及以下四个必要条件:

  1. 互斥条件(Mutual Exclusion):资源只能同时被一个进程占用,其他进程需要等待。

  2. 占有且等待(Hold and Wait):进程在等待其他资源的同时,仍然保持对已分配资源的占有。

  3. 不可抢占(No Preemption):已经分配给一个进程的资源不能被其他进程抢占,只能由持有者进程主动释放。

  4. 循环等待(Circular Wait):存在一个进程链,每个进程都在等待下一个进程所持有的资源。

Case 1 : 没有释放锁

Case 2 : 单线程重复申请锁

Case 3 : 双线程多锁申请

避免死锁的一般建议,就是让两个互斥量总以相同的顺序上锁:总在互斥量B之前锁住互斥量 A,就永远不会死锁。当然前提是不同的互斥量是用于不同的地方

当有多个互斥量保护同一个类的独立实例时,如果一个操作需要对同一个类的两个不同实例进行数据交换操作,为了保证数据交换操作的正确性,需要避免数据被并发修改,并确保每个实例上的互斥量都能锁住自己要保护的区域。

然而,此时选择一个固定的顺序来上锁可能会导致问题。例如,假设选择第一个实例提供的互斥量作为第一个参数,第二个实例提供的互斥量作为第二个参数。如果有两个线程,它们都试图对相同的两个实例之间进行数据交换,但是它们接收的参数顺序不同,程序就可能会陷入死锁状态:

线程1锁住A,尝试锁住B,而线程2锁住B,尝试锁住A,导致两个线程相互等待对方释放锁而无法继续执行。

std::lock

std::lock可以一次性锁住多个互斥量,并且没有死锁风险。因此上述代码可以这样修改:

首先使用std::lock以相同的顺序获取两个互斥量的锁,然后使用std::adopt_lock标志将锁的所有权交给lock_guard。修改后的代码确保了以相同的顺序获取锁,从而避免了死锁的发生。无论线程以什么样的顺序进入swap函数,都能按照相同的顺序获取锁,避免了互相等待对方释放锁的情况。

std::adopt_lock

adopt_lock是一个参数,用于在构造lock_guard对象时指示它已经拥有互斥锁的所有权。当lock_guard对象被创建时,它会自动对互斥锁进行上锁,并在析构时自动解锁。而使用adopt_lock参数,它假设调用者已经拥有了互斥锁的所有权,因此在构造函数中不会尝试再次上锁。

一般情况下,使用lock_guard来管理互斥锁时,会在创建对象时自动进行上锁操作。但是,在某些情况下,例如多个互斥锁需要同时上锁时,可以先使用std::lock函数将所有的互斥锁一起加锁,然后再通过lock_guard对象管理这些已经加锁的互斥锁。这时就可以使用adopt_lock参数告诉lock_guard对象,它已经拥有互斥锁的所有权,无需再次上锁。

死锁的评论 (共 条)

分享到微博请遵守国家法律