【问题标题】:Behavior of condition_variable_any when used with a recursive_mutex?与 recursive_mutex 一起使用时 condition_variable_any 的行为?
【发布时间】:2012-08-01 03:23:14
【问题描述】:

当使用condition_variable_anyrecursive_mutex 时,recursive_mutex 是否可以在condition_variable_any::wait 等待时通常从其他线程获取?我对 Boost 和 C++11 的实现都感兴趣。

这是我主要关心的用例:

void bar();

boost::recursive_mutex mutex;
boost::condition_variable_any condvar;

void foo()
{
    boost::lock_guard<boost::recursive_mutex> lock(mutex);
    // Ownership level is now one

    bar();
}

void bar()
{
    boost::unique_lock<boost::recursive_mutex> lock(mutex);
    // Ownership level is now two

    condvar.wait(lock);
   // Does this fully release the recursive mutex,
   // so that other threads may acquire it while we're waiting?
   // Will the recursive_mutex ownership level
   // be restored to two after waiting?
}

【问题讨论】:

  • 我在 Boost 文档中找不到任何关于混合 condition_variable_anyrecursive_mutex 的内容。
  • 您是否期望(或希望)调用wait 来解锁互斥锁?
  • @curiousguy :是的,我确实做到了。这种行为会降低它遇到死锁问题的可能性。
  • 调用函数呢?它期望 bar() 解锁互斥锁吗?这里的问题是bar() 的合同允许它与其他地方的其他锁 混淆。如果你想要这是你的设计,你需要让它非常明确。
  • @curiousguy,我没有在我的任何东西中使用recursive_mutex。我刚刚在这里阅读另一个问题时遇到了它,想知道它如何与条件变量互操作。

标签: c++ multithreading c++11 boost recursive-mutex


【解决方案1】:

通过对 Boost 文档的严格解释,我得出结论,condition_variable_any::wait一般不会导致 recursive_mutex 在等待通知时被其他线程获取。

班级condition_variable_any

template&lt;typename lock_type&gt; void wait(lock_type&amp; lock)

效果:

以原子方式调用lock.unlock() 并阻塞当前线程。当调用this-&gt;notify_one() 或通知线程时,线程将解除阻塞 this-&gt;notify_all(),或者是虚假的。当线程被解除阻塞时(对于 不管什么原因),通过调用lock.lock()重新获取锁 在调用等待返回之前。锁也被重新获取 如果函数因异常退出,则调用 lock.lock()

所以condvar.wait(lock) 将调用lock.unlock,而后者又调用mutex.unlock,这会将所有权级别降低一(不一定降至零)。


我已经编写了一个测试程序来证实我的上述结论(对于 Boost 和 C++11):

#include <iostream>

#define USE_BOOST 1

#if USE_BOOST

#include <boost/chrono.hpp>
#include <boost/thread.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/recursive_mutex.hpp>
namespace lib = boost;

#else

#include <chrono>
#include <thread>
#include <condition_variable>
#include <mutex>
namespace lib = std;

#endif

void bar();


lib::recursive_mutex mutex;
lib::condition_variable_any condvar;
int value = 0;

void foo()
{
    std::cout << "foo()\n";
    lib::lock_guard<lib::recursive_mutex> lock(mutex);
    // Ownership level is now one

    bar();
}

void bar()
{
    std::cout << "bar()\n";
    lib::unique_lock<lib::recursive_mutex> lock(mutex);
    // Ownership level is now two

    condvar.wait(lock); // Does this fully release the recursive mutex?

    std::cout << "value = " << value << "\n";
}

void notifier()
{
    std::cout << "notifier()\n";
    lib::this_thread::sleep_for(lib::chrono::seconds(3));
    std::cout << "after sleep\n";

    // --- Program deadlocks here ---
    lib::lock_guard<lib::recursive_mutex> lock(mutex);

    value = 42;
    std::cout << "before notify_one\n";
    condvar.notify_one();
}

int main()
{
    lib::thread t1(&foo); // This results in deadlock
    // lib::thread t1(&bar); // This doesn't result in deadlock
    lib::thread t2(&notifier);
    t1.join();
    t2.join();
}

我希望这可以帮助其他在混合condition_variable_anyrecursive_mutex 时面临同样困境的人。

【讨论】:

  • 想解释一下否决票?我的推理有问题吗?
  • 不是我的反对意见,但我支持它,因为答案没有回答真正的问题,只是声称它无法解决。问题应该是“我如何使用带有 recursive_mutex 的 condition_var”
  • 大吃一惊。 +1 假定为故意欢闹。
  • +1 C++11 规范与 boost 文档非常相似。这是 C++ 草案(当时)之后的罕见提升案例,反之亦然。 condition_variable_any 是在open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2447.htm 中提出的,当时的规范要求递归锁的锁计数在进入等待时为一个。那个具体的措辞已经改变,但意图没有改变。 boost 然后随后将其捡起。
【解决方案2】:

您可以通过将参数allowed_unlock_count 添加到对mutex 对象进行操作的每个函数来修复此设计;关于allowed_unlock_count,有两种类型的保证:

(permit-unlock-depth) allowed_unlock_count代表mutex允许解锁的深度:调用者允许bar解锁互斥锁allowed_unlock_count次。解锁后,不保证mutex的状态。

(promise-unlock) allowed_unlock_count代表mutex的锁定深度:调用者保证解锁mutex正好allowed_unlock_count次将允许其他线程抢到@987654333 @对象。

这些保证是函数的前置条件和后置条件

这里bar 依赖于(promise-unlock)

// pre: mutex locking depth is allowed_unlock_count
void bar(int allowed_unlock_count)
{
    // mutex locking depth is allowed_unlock_count
    boost::unique_lock<boost::recursive_mutex> lock(mutex);
    // mutex locking depth is allowed_unlock_count+1

    // you might want to turn theses loops
    // into an a special lock object!
    for (int i=0; i<allowed_unlock_count; ++i)
        mutex.unlock();
    // mutex locking depth is 1

    condvar.wait(lock); // other threads can grab mutex

    // mutex locking depth is 1
    for (int i=0; i<allowed_unlock_count; ++i)
        mutex.lock();
    // mutex locking depth is allowed_unlock_count+1
}
// post: mutex locking depth is allowed_unlock_count

被调用函数必须明确允许调用者减少锁定深度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多