【问题标题】:How do std::unique_lock and std::condition_variable workstd::unique_lock 和 std::condition_variable 如何工作
【发布时间】:2015-08-10 07:11:36
【问题描述】:

我需要澄清一下 lock 和 condition_variable 是如何工作的。

在此处的 -slightly modified- 代码中 cplusplusreference

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 已经锁定了互斥锁,主线程如何锁定它。

this answer我看到是因为cv.wait解锁互斥体。

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

例如,我可以这样做吗?

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

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

现在有什么不同吗?

在这种情况下,我不明白为什么会收到“运行时错误”here

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

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

      【讨论】:

        【解决方案3】:

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

        锁存在的基本原因是提供互斥。互斥保证代码的某些部分仅由单个线程执行。这就是为什么您不能只等到稍后再锁定 - 您需要锁定它才能获得保证。

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

        【讨论】:

        • “保证当你再次醒来时,你会得到它” cv.notify 之后,它是否会被等待 cv 的人再次自动锁定?我理解正确吗?如果同时有人锁定了它怎么办?它是否还需要再次等待,虽然我只希望它等待 cv,而不是再次锁定互斥锁?
        • “解除阻塞时,不管什么原因,重新获取锁并等待退出。”,引自en.cppreference.com/w/cpp/thread/condition_variable/wait
        • 在内部,我希望条件变量的代码尝试获取锁并阻塞,直到获取完成。这意味着是的,如果其他线程设法在等待线程之前获得锁,它将不得不等待更长的时间。
        • 请提供一个示例,说明您何时希望等待条件变量而不拥有锁。
        • 我想像使用延迟对象一样使用 cond var。例如,当我想获得在另一个线程中运行的函数的结果时,该线程之前已经启动。一旦我得到结果,我就没有兴趣再次获得锁定。抱歉,我现在写的很匆忙,但如果字面上不清楚,我可以稍后提供代码示例。
        猜你喜欢
        • 2012-11-02
        • 2020-11-15
        • 2012-10-17
        • 1970-01-01
        • 1970-01-01
        • 2020-03-28
        • 1970-01-01
        • 2013-12-29
        • 1970-01-01
        相关资源
        最近更新 更多