4

で使用condition_variable_anyする場合recursive_mutex、待機中に他のスレッドから一般的にrecursive_mutex取得できますか?BoostとC++11の両方の実装に興味があります。condition_variable_any::wait

これは私が主に懸念しているユースケースです:

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?
}
4

2 に答える 2

6

Boostのドキュメントを厳密に解釈することにより、通知を待っている間、他のスレッドが取得できるようにcondition_variable_any::waitなることは一般的にないと結論付けました。recursive_mutex

クラスcondition_variable_any

template<typename lock_type> void wait(lock_type& lock)

効果:

アトミックに呼び出しlock.unlock()て、現在のスレッドをブロックします。this->notify_one()スレッドは、または への呼び出しによって通知されたときthis->notify_all()、または誤ってブロックを解除します。スレッドのブロックが解除されると(何らかの理由で)、lock.lock() 待機の呼び出しが戻る前に呼び出すことによってロックが再取得されます。lock.lock()関数が例外で終了した場合に呼び出すことによって、ロックも再取得されます。

したがって、condvar.wait(lock)を呼び出しますlock.unlock。これにより、が呼び出さmutex.unlockれ、所有権レベルが1つ下がります(必ずしもゼロになるとは限りません)。


上記の結論を確認するテストプログラムを作成しました(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_anyこれが、とを混合するときに同じジレンマに直面している他の人に役立つことを願っていますrecursive_mutex

于 2012-08-01T04:19:39.513 に答える
1

オブジェクトallowed_unlock_countを操作するすべての関数にパラメーターを追加することで、この設計を修正できます。mutex保証には2つのタイプがあります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回数ロックを解除すると、他のスレッドがmutexオブジェクトを取得できることを保証します。

これらの保証は、関数の事前条件と事後条件です。

ここでは(promise-unlock)barに依存します:

// 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

呼び出された関数は、呼び出し元によるロックの深さを減らすことを明示的に許可する必要があります。

于 2012-08-17T01:37:21.603 に答える