代码之家  ›  专栏  ›  技术社区  ›  Aquarius_Girl

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

  •  0
  • Aquarius_Girl  · 技术社区  · 8 年前

    https://en.cppreference.com/w/cpp/thread/lock_tag

    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;
    }
    

    同时锁定两个互斥锁有什么好处?
    他们把门锁在这里有什么好处?

    请解释他们的决定背后的原因。

    4 回复  |  直到 8 年前
        1
  •  1
  •   Aconcagua    8 年前

    打开分机 Richard Hodges 的 answer

    理查德已经很好地解释过了,只是说得更清楚了一点:我们这样可以避免死锁( std::lock 实现时不会发生死锁)。

    他们在这里延迟锁有什么好处?

    延迟锁将导致无法立即获取它。这很重要,因为如果他们 做 标准::锁定 然后实现)。

    std::lock ):

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

    旁注:另一个更简单的避免死锁的算法总是先用较低的账号(AN)锁定银行账户。如果一个线程正在等待更高的AN的锁,那么持有它的另一个线程要么已经获得了这两个锁,要么正在等待第二个锁,后者不能是第一个线程之一,因为它必须拥有更高的AN。

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

        2
  •  7
  •   Richard Hodges    8 年前

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

    问题是,当锁定不止一个东西时,每个储物柜必须按相同的顺序锁定和解锁,否则我们就会陷入死锁。

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

    std::lock

    标准::锁定

    std::lock_guard<> 打开它所指的互斥锁(见RAII)。这使得代码在所有情况下都能正确运行—即使出现异常,可能会导致在代码不经过如下语句的情况下提前退出当前代码块 to.m.unlock()

    https://wiki.sei.cmu.edu/confluence/display/cplusplus/CON53-CPP.+Avoid+deadlock+by+locking+in+a+predefined+order

        3
  •  0
  •   cha0site    8 年前

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

    std::lock ,因为这样做可以确保不会出现死锁。

    完成交易后,我们需要确保解除锁定。这个代码是用RAII实现的。与 adopt_lock 标签,我们使对象采用一个已经锁定的互斥锁(当 lock1 超出范围)。 defer_lock 标签,我们创建一个 unique_lock 对于当前未锁定的互斥体,打算稍后锁定它。同样,当 超出范围。

        4
  •  0
  •   Aconcagua    8 年前

    from to 是 2 可以使用的帐户 分开。

    没有人 使用 也不是 当你做转账的时候。

    lock_guard 当 退出 从函数。