Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/138.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我要在一个函数中锁定两个互斥锁?这也需要延迟锁定?_C++_Std_Mutex - Fatal编程技术网

C++ 为什么我要在一个函数中锁定两个互斥锁?这也需要延迟锁定?

C++ 为什么我要在一个函数中锁定两个互斥锁?这也需要延迟锁定?,c++,std,mutex,C++,Std,Mutex,无效转账(银行账户和发件人、银行账户和收件人、整数金额) { //在没有死锁的情况下锁定两个互斥锁 std::lock(从.m到.m); //确保已锁定的两个互斥锁在作用域末尾都已解锁 std::锁紧/防护锁1(从.m开始,std::采用锁紧); std::锁紧/防护锁2(to.m,std::采用锁紧); //等效方法: //std::unique_lock lock1(from.m,std::defer_lock); //std::unique_lock 2(to.m,std::defer_l

无效转账(银行账户和发件人、银行账户和收件人、整数金额)
{
//在没有死锁的情况下锁定两个互斥锁
std::lock(从.m到.m);
//确保已锁定的两个互斥锁在作用域末尾都已解锁
std::锁紧/防护锁1(从.m开始,std::采用锁紧);
std::锁紧/防护锁2(to.m,std::采用锁紧);
//等效方法:
//std::unique_lock lock1(from.m,std::defer_lock);
//std::unique_lock 2(to.m,std::defer_lock);
//锁(锁1,锁2);
from.balance-=金额;
to.余额+=金额;
}
同时锁定两个互斥锁有什么好处?
他们在这里延迟锁有什么好处


请解释他们做出这一决定的原因。

from
to
2
帐户,可在应用程序中的任何位置单独使用

通过为每个帐户设置互斥,您可以确保在您进行转账时,没有人使用
from
to
帐户


lock\u guard
退出功能时将释放互斥锁。

如果我修改银行帐户而不锁定它,其他人可以同时尝试修改它。这是一场比赛,结果将是未定义的行为(通常丢失或神奇地创造金钱)

转账时,我修改了2个银行账户。所以它们都需要被锁定

问题是,当锁定多个东西时,每个储物柜必须以相同的顺序锁定和解锁,否则就会出现死锁

当它是银行账户时,锁没有自然的顺序。成千上万的线程可以向各个方向转移资金

因此,我们需要一种锁定多个互斥体的方法来解决这个问题,这就是
std::lock

std::lock
仅锁定互斥锁-它不保证在退出当前代码块时解锁

std::lock\u guard
在销毁时解锁它所指的互斥锁(参见RAII)。这使得代码在所有情况下都能正常运行,即使出现异常,可能会导致在没有代码流过语句(如
to.m.unlock()


这里有一个很好的解释(带示例):

银行帐户数据结构对每个帐户都有一个锁

当把钱从一个帐户转到另一个帐户时,我们需要锁定两个帐户(因为我们从一个帐户中取出钱,然后把钱加到另一个帐户中)。我们希望此操作不会死锁,因此使用
std::lock
立即锁定这两个操作,因为这样做可以确保不会出现死锁

完成事务后,我们需要确保释放锁。这段代码使用RAII实现这一点。使用
adoption\u lock
标记,我们使对象采用一个已经锁定的互斥锁(当
lock1
超出范围时,将释放该互斥锁)。 使用
defer\u lock
标记,我们为当前解锁的互斥锁创建一个
unique\u lock
,以便稍后锁定它。同样,当
唯一锁
不在范围内时,它将被解锁。

的扩展

同时锁定两个互斥锁有什么好处

Richard已经很好地解释了这一点,只是解释得更明确一点:我们通过这种方式避免死锁(
std::lock
的实现使得死锁不会发生)

他们在这里通过延迟锁定获得了什么

延迟锁定会导致无法立即获取它。这一点很重要,因为如果他们这样做了,他们只会在没有任何死锁保护的情况下进行操作(随后的
std::lock
实现了这一点)

关于死锁避免(请参阅):

使用死锁避免算法锁定给定的可锁定对象lock1、lock2、…、lockn以避免死锁

对象被一系列未指定的锁定、尝试锁定和解锁调用锁定。[……]

旁注:另一个更简单的避免死锁的算法是始终使用e锁定银行帐户。G先降低账号(AN)。如果一个线程正在等待更高AN的锁,那么持有它的另一个线程要么已经获得了两个锁,要么正在等待第二个锁——这不能是第一个线程中的一个,因为它必须具有更高的AN


对于任意数量的线程,这并没有多大变化,任何持有较低锁的线程都在等待较高的锁(如果持有的话)。如果你画一个有向图,边从a到B,如果a正在等待B持有的第二个锁,你将得到一个(多)树结构,但你永远不会有循环子结构(这将表示死锁)。

你总是可以先用更低(或更高)的帐号锁定帐户,这已经可以防止死锁。感谢您对多锁的解释。如果你能使用延迟锁来完成答案,那就太好了。谢谢。这意味着std::lock是唯一实现了死锁避免算法的锁。std::unique_lock没有避免死锁的功能?@Aquarius_Girl没有
std::unique_lock
设计为只锁定一个互斥锁-在您创建互斥锁(不延迟)或通过调用
lock
告诉互斥锁(延迟)或最终
尝试锁定互斥锁时锁定。同样适用于
std::lock\u guard
(除了不提供延迟锁之外)。如果您将构造函数(接受一个互斥参数)与函数的参数(多个可锁定对象)进行比较,这种差异就会变得明显。@Aquarius_Girl,让我明确地提示您,在一个方面,类之间存在更基本的差异(根据RAII原理,析构函数解锁)而
std::lock
是一个“仅”功能,没有解锁功能…实际上是什么
void transfer(bank_account &from, bank_account &to, int amount)
{
    // lock both mutexes without deadlock
    std::lock(from.m, to.m);
    // make sure both already-locked mutexes are unlocked at the end of scope
    std::lock_guard<std::mutex> lock1(from.m, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(to.m, std::adopt_lock);

// equivalent approach:
//    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
//    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
//    std::lock(lock1, lock2);

    from.balance -= amount;
    to.balance += amount;
}