web-dev-qa-db-ja.com

lock_guardのロックを手動で解除するのは未定義/悪い設計ですか?

私はこのようなコードを持っています:

do {
    lock_guard<mutex> lck(globalMtx);
    auto itr = someMap.end();
    for (/*conditions*/){
        //do stuff with itr and someMap
        // if a certain condition is met, we exit function with a return
        // globalMtx needs to be unlocked at that time
    }
    if (itr == someMap.end()){
        // I need to unlock the globalMtx here
        globalMtx.unlock()
        // A command is sent to modify someMap before we try again
        this_thread::sleep_for( chrono::seconds( 5 ) );
    } else {
        break;
    }
} while (true);

Ifスコープでわかるように、globalMtxのロックを解除して、「someMap」を変更してから再度実行できるようにする必要があります。私は多くのスレッド/フォーラム/mutex.lock()/ unlock()を使用してミューテックスを手動でロックすることは悪い考えであり、通常c ++ 11以降ではもう行われないことを読みました。

では、この場合、スコープを離れるとミューテックスがロックされたままになるのを防ぎながら、必要に応じてミューテックスを制御するにはどうすればよいでしょうか。

7
user1324674

いいえ、この場合、std::mutex::unlock()を直接呼び出さないでください。_std::lock_guard_デストラクタが再びstd::mutex::unlock()を呼び出し、UBにつながるためです。代わりに std :: unique_lock を使用できます。これは、_std::lock_guard_のように軽量ではありませんが、unlock()を呼び出すことができます。

_std::unique_lock<mutex> lck(globalMtx);
...
lck.unlock(); // lck object is aware that globalMtx is released and would do nothing in it's dtor after this
_
12
Slava

はい、それは悪い考えです。_lock_guard_は、破壊されたときにミューテックスのロックを解除するため、ミューテックスは2回ロック解除されることになります。これにより、未定義の動作が発生します(つまり、運が良ければクラッシュします)。

代わりに、_std::unique_lock_を使用してください。これにはunlock()メソッドがあり、基礎となるミューテックスのunlockメソッドを呼び出すだけでなく、_unique_lock_オブジェクトがミューテックスのロックを再度解除しないことも保証します。

3
Arthur Tacca

ミューテックスが保持されているかどうかを追跡し、保持されている場合にのみ破棄時にロックを解除するlock_guardのようなラッパーを記述します。ラッパーには、ロック解除と再ロックの両方が必要です(ロックがすでに保持されている場合は何もしません)。

1
SoronelHaetir