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

时间:2015-08-10 07:11:37

标签: c++ c++11 concurrency mutex condition-variable

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

在此处{-3}}

的-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();
}

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

cplusplusreference我发现这是因为cv.wait 解锁互斥锁。

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

例如,我可以这样做吗?

std::unique_lock<std::mutex> lk(m, std::defer_lock);

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

现在有什么不同吗?

我不知道为什么会出现&#34;运行时错误&#34;在这种情况下this answer

3 个答案:

答案 0 :(得分:6)

引自std::condition_variable::wait()

  

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

答案 1 :(得分:3)

我将尝试为WHY条件变量需要锁定添加更多解释。

您必须拥有锁,因为您的代码需要检查条件谓词是否为真。谓词是某些值或值的组合,为了继续,必须为真。它可以是一个NULL指针或指向已准备好使用的已完成数据结构。

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

条件通知和等待返回并不意味着条件为真。它只意味着条件在某个时候是真的。它甚至可能是真的,然后是假的,然后又是真的。这也可能意味着你的线程已经在一个不相关的信号处理程序中导致条件等待突破。您的代码甚至不知道调用条件通知的次数。

因此,一旦条件等待返回它,就锁定互斥锁。现在,您的代码可以安全地在锁中检查条件。如果为true,则代码可以更新更新所需的内容并释放锁定。如果不是这样,它只是回到条件等待再试一次。例如,它可以采用该数据结构指针并将其复制到向量中,然后将锁保护指针设置回NULL。

将条件视为使轮询循环更有效的一种方法。你的代码仍然必须完成它在循环等待中运行的所有事情,除了它可以进入休眠而不是不停的旋转。

答案 2 :(得分:1)

我认为你的误解源于对锁是什么以及它们如何与条件变量相互作用的更深层次的误解。

存在锁定的基本原因是提供互斥。相互排除保证代码的某些部分仅由单个线程执行。这就是为什么你不能等到锁定之后 - 你需要锁定以获得保证。

如果您希望代码的某些其他部分执行但在执行当前代码时仍需要互斥,则会导致问题。这是条件变量派上用场的地方:它们提供了一种结构化方式来释放锁定,并保证当你再次醒来时你会恢复它。这就是在等待功能中解锁的原因。