【问题标题】:Does wait_until work differently in main thread wrt not main one? c++wait_until 在主线程中的工作方式与主线程不同吗? C++
【发布时间】:2019-09-18 09:34:16
【问题描述】:

在一个单独的主线程中执行相同的代码,条件变量的行为不同

#include <iostream>
#include <condition_variable>
#include <mutex>
#include <chrono>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;
void waits()
{
    std::mutex mCvMtx;
    std::condition_variable mCondVar;

   auto now = std::chrono::system_clock::now();

    std::unique_lock<std::mutex> lk(mCvMtx);
    if(mCondVar.wait_until(lk, now+ 3*1000ms) ==  cv_status::timeout)
    {
        cout << "Fire";
    }
    else
    {
        cout << "Condition variable notified ";
    }
    now = std::chrono::system_clock::now();
}

int main()
{

     std::thread t1(waits);
     t1.join(); 

    return 0;
}
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <chrono>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;

int main()
{
     std::mutex mCvMtx;
    std::condition_variable mCondVar;

   auto now = std::chrono::system_clock::now();

    std::unique_lock<std::mutex> lk(mCvMtx);
    if(mCondVar.wait_until(lk, now+ 3*1000ms) ==  cv_status::timeout)
    {
        cout << "Fire";
    }
    else
    {
        cout << "Condition variable notified ";
    }
    now = std::chrono::system_clock::now();

    return 0;
}

我不明白为什么在第一个示例中输出结果为“Fire”(因此未通知 cv 并等待我指定的时间),而在第二个示例中,我在主线程中执行相同的代码,输出结果“通知条件变量”,无需等待任何秒。

你有什么解释吗?谢谢

【问题讨论】:

  • 不相关,但不要使用system_clock测量时间,更喜欢steady_clock 代替(系统时钟可以调整,例如通过ntp更新)。跨度>

标签: c++ multithreading c++11 condition-variable


【解决方案1】:

因为spurious wakeups

虚假唤醒描述了使用条件的复杂性 某些多线程 API(例如 POSIX)提供的变量 线程和 Windows API。

即使在条件变量似乎已从 等待线程的观点,等待的条件可能 还是假的。造成这种情况的原因之一是虚假唤醒。那 也就是说,一个线程可能会从它的等待状态中被唤醒,即使没有 线程向条件变量发出信号。为了正确,它是 那么,有必要在 线程已完成等待。因为可能会发生虚假唤醒 重复地,这是通过在一个终止的循环内等待来实现的 当条件为真时

进一步阅读:

C++ Core Guidelines: Be Aware of the Traps of Condition Variables

【讨论】:

  • 是的,我想过,但可以系统化吗?我的意思是,我每次运行该代码至少 10 次,结果始终相同(在前一种情况下没有虚假唤醒,并且总是在几秒钟内)......我给自己的解释是,也许这个虚假唤醒确实是系统的,线程的创建需要适当的时间在我进入等待条件之前让它通过......但我不知道这是否有意义。你同意我的理论吗? ps.谢谢你的回答
  • @SeriouslLancerl 影响虚假唤醒的机制和所有因素都非常复杂,远高于我的薪酬等级。只有在低级系统架构、同步机制和编译器实现方面有经验的专家才能回答这个问题。
【解决方案2】:

条件变量只是一种没有状态的通知机制,因此在没有服务员时通知会丢失,而在没有发出通知时虚假唤醒会解除阻塞。

您必须等待共享状态发生变化。例如:

std::mutex m;
std::condition_variable c;

// Only ever read or write shared_state when the mutex is locked.
// Otherwise race conditions create a deadlock.
bool shared_state = false;

// Waiting thread.
void wait() {
    std::unique_lock<std::mutex> l(m);
    while(!shared_state) // Also handles spurious wake ups.
        c.wait(l);
    // shared_state is true, mutex is locked.
}

// Notifying thread.
void wait() {
    {
        std::unique_lock<std::mutex> l(m);
        shared_state = true;
    }
    c.notify_one();
}

【讨论】:

  • 另外,有一个带有谓词作为附加参数的等待函数:c.wait(l, []() { return shared_state; });
  • @Aconcagua 让事情变得更大更复杂很容易。很难让它们变得更好。想想碳足迹,否则不必要,创建一个独特的 lambda 类,该类的 lambda 对象和优化器优化它。
  • 有了这样一个简单的谓词,我假设编译器会再次优化任何开销......也就是说,我只是想展示另一种方式存在('或者'),没有任何条件,无论好坏,留给读者......
猜你喜欢
  • 2021-01-31
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
  • 2013-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多