死锁(Deadlock)是指在并发系统中,两个或多个进程(线程)互相持有对方所需资源而无法继续执行的状态。这种情况下,进程将永远等待下去,直到外部干预才能解除死锁。
死锁通常涉及以下四个必要条件:
互斥条件(Mutual Exclusion):资源只能同时被一个进程占用,其他进程需要等待。
【资料图】
占有且等待(Hold and Wait):进程在等待其他资源的同时,仍然保持对已分配资源的占有。
不可抢占(No Preemption):已经分配给一个进程的资源不能被其他进程抢占,只能由持有者进程主动释放。
循环等待(Circular Wait):存在一个进程链,每个进程都在等待下一个进程所持有的资源。
Case 1 : 没有释放锁
Case 2 : 单线程重复申请锁
Case 3 : 双线程多锁申请
避免死锁的一般建议,就是让两个互斥量总以相同的顺序上锁:总在互斥量B之前锁住互斥量A,就永远不会死锁。当然前提是不同的互斥量是用于不同的地方。
当有多个互斥量保护同一个类的独立实例时,如果一个操作需要对同一个类的两个不同实例进行数据交换操作,为了保证数据交换操作的正确性,需要避免数据被并发修改,并确保每个实例上的互斥量都能锁住自己要保护的区域。
然而,此时选择一个固定的顺序来上锁可能会导致问题。例如,假设选择第一个实例提供的互斥量作为第一个参数,第二个实例提供的互斥量作为第二个参数。如果有两个线程,它们都试图对相同的两个实例之间进行数据交换,但是它们接收的参数顺序不同,程序就可能会陷入死锁状态:
std::lock可以一次性锁住多个互斥量,并且没有死锁风险。因此上述代码可以这样修改:
首先使用std::lock以相同的顺序获取两个互斥量的锁,然后使用std::adopt_lock标志将锁的所有权交给lock_guard。修改后的代码确保了以相同的顺序获取锁,从而避免了死锁的发生。无论线程以什么样的顺序进入swap函数,都能按照相同的顺序获取锁,避免了互相等待对方释放锁的情况。
adopt_lock是一个参数,用于在构造lock_guard对象时指示它已经拥有互斥锁的所有权。当lock_guard对象被创建时,它会自动对互斥锁进行上锁,并在析构时自动解锁。而使用adopt_lock参数,它假设调用者已经拥有了互斥锁的所有权,因此在构造函数中不会尝试再次上锁。
一般情况下,使用lock_guard来管理互斥锁时,会在创建对象时自动进行上锁操作。但是,在某些情况下,例如多个互斥锁需要同时上锁时,可以先使用std::lock函数将所有的互斥锁一起加锁,然后再通过lock_guard对象管理这些已经加锁的互斥锁。这时就可以使用adopt_lock参数告诉lock_guard对象,它已经拥有互斥锁的所有权,无需再次上锁。
关键词:
Copyright 2000-2021 by www.jiaoyu.zuojing.com all rights reserved
邮箱 : 85 18 07 48 3@qq.com