将 unique_lock<recursive_mutex> 移动到另一个线程

Moving a unique_lock<recursive_mutex> to another thread

我想知道当你移动一个包含 recursive_mutexunique_lock 时会发生什么。

具体来说,我在看这段代码:

recursive_mutex g_mutex;

#define TRACE(msg) trace(__FUNCTION__, msg)

void trace(const char* function, const char* message)
{
    cout << std::this_thread::get_id() << "\t" << function << "\t" << message << endl;
}

future<void> foo()
{
    unique_lock<recursive_mutex> lock(g_mutex);
    TRACE("Owns lock");
    auto f = std::async(launch::async, [lock = move(lock)]{
        TRACE("Entry");
        TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Owns lock! 
        this_thread::sleep_for(chrono::seconds(3));
    });
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Doesn't own lock! 
    return f;
}


int main()
{
    unique_lock<recursive_mutex> lock(g_mutex);
    TRACE("Owns lock");
    auto f = foo();    
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!");        // Prints Owns lock! 
    f.wait();
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!");        // Prints Owns lock!
}

这个示例代码的输出让我很惊讶。 main()中的unique_lock怎么知道线程释放了互斥体呢?是真的吗?

您似乎将某些魔法属性归于 unique_lock。没有,很简单class。它有两个数据成员,Mutex* pmbool owns(成员名称仅供说明)。 lock() 就是 pm->lock(); owns = true;,而 unlock 就是 pm->unlock(); owns = false;。析构函数是 if (owns) unlock();。 Move constructor 复制了这两个成员,并将它们在原来的 nullptrfalse 中相应地设置。 owns_lock() returns owns 成员的值。

所有线程同步的魔力都在互斥锁本身及其 lock()unlock() 方法中。 unique_lock 只是它的薄包装。

现在,作为先决条件,调用 mutex.unlock() 的线程必须持有互斥量(也就是说,该线程之前已对其调用 lock()),否则程序会出现未定义的行为。无论您是显式调用 unlock,还是欺骗像 unique_lock 这样的助手来为您调用它,都是如此。

鉴于这一切,将一个 unique_lock 实例移到另一个线程只是此后不久触发未定义行为的一种方法;没有上升空间。