std :: unique_lock和std :: condition_variable如何工作

Mer*_*tce 5 c++ concurrency mutex condition-variable c++11

我需要弄清楚lock和condition_variable是如何工作的.

在这里cminplusreference的-slightly modified-code中

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});

    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";

    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}

int main()
{
    std::thread worker(worker_thread);
    std::this_thread::sleep_for(std::chrono::seconds(1));

    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();

    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';

    worker.join();
}
Run Code Online (Sandbox Code Playgroud)

我感到困惑的是,如果worker_thread已经锁定它,主线程如何锁定互斥锁.

这个答案我看到它是因为cv.wait 解锁互斥锁.

但是现在我对此感到困惑:那么为什么我们需要锁定它,如果cv.wait解锁呢?

例如,我可以这样做吗?

std::unique_lock<std::mutex> lk(m, std::defer_lock);
Run Code Online (Sandbox Code Playgroud)

所以,我创建了锁对象,因为cv需要它,但我在创建时不会锁定它.

现在有什么不同吗?

我不明白为什么我收到"运行时错误" 这里在这种情况下.

Ric*_*lly 7

引用自std :: condition_variable :: wait():

如果lock.mutex()未被当前线程锁定,则调用此函数是未定义的行为.


Zan*_*ynx 4

我将尝试添加更多关于为什么条件变量需要锁的解释。

您必须拥有锁,因为您的代码需要检查条件谓词是否为真。谓词是某个值或值的组合,必须为 true 才能继续。它可以是 NULL 指针或指向可供使用的完整数据结构。

您必须在等待之前锁定它并检查谓词,因为当您开始等待条件时,另一个线程可能已经设置了它。

条件通知和等待返回并不意味着条件为真。它仅意味着该条件在某个时候为真。它甚至可能是真的,然后又是假的,然后又是真的。这也可能意味着您的线程处于不相关的信号处理程序中,导致条件等待爆发。您的代码甚至不知道条件通知已被调用多少次。

因此,一旦条件等待返回,它就会锁定互斥体。现在,您的代码可以在安全地锁在锁中时检查情况。如果为 true,则代码可以更新它需要更新的内容并释放锁。如果不是真的,它就会返回到条件等待重试。例如,它可以获取该数据结构指针并将其复制到向量中,然后将受锁保护的指针设置回 NULL。

将条件视为提高轮询循环效率的一种方法。您的代码仍然必须完成它在循环等待中运行时要做的所有事情,除了它可以进入睡眠状态而不是不停地旋转。