lock_guard 手动解锁 undefined/bad 设计?

Is unlocking a lock_guard manually undefined/bad design?

我有这样的代码:

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"。我在许多 threads/forums/whatever 中读到,使用 mutex.lock()/unlock() 手动锁定互斥锁是一个坏主意,通常再也不会用 c++11 或更高版本了。

那么在这种情况下,我该怎么做才能根据需要控制互斥量,同时仍然防止任何离开作用域导致互斥量被锁定的情况?

写一个像 lock_guard 这样的包装器来跟踪互斥锁是否被持有,并且只有在它被持有时才在销毁时解锁包装器应该有解锁和重新锁定(这是一个空操作,如果锁已经被持有了)..

不,在这种情况下您不应该直接调用 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

是的,这是个坏主意,因为 lock_guard 在销毁时仍会解锁互斥锁,因此互斥锁最终会被解锁两次。这会导致未定义的行为(即如果幸运的话会崩溃)。

而是使用 std::unique_lock。这个有一个unlock()方法,不仅调用了底层互斥量的unlock方法,而且保证了unique_lock对象不会对互斥量进行二次解锁