繁体   English   中英

C ++:如何在UI线程和工作程序std :: thread之间使用std :: condition_variable

[英]C++ : How to use an std::condition_variable between UI thread & worker std::thread

我正在尝试使用C++11std :: condition_variable进行UI线程和工作线程之间的数据事务。

情况:
m_calculated_value是在复杂逻辑之后计算的值。 UI线程触发事件时需要这样做。 UI线程调用MyClass::GetCalculatedValue获取的价值m_calculated_value需要由工作线程函数,计算MyClass::ThreadFunctionToCalculateValue

码:

std::mutex              m_mutex;
std::condition_variable m_my_condition_variable;
bool                    m_value_ready;
unsigned int            m_calculated_value;


// Gets called from UI thread
unsigned int MyClass::GetCalculatedValue() {

    std::unique_lock<std::mutex> lock(m_mutex);
    m_value_ready = false;

    m_my_condition_variable.wait(lock, std::bind(&MyClass::IsValueReady, this));

    return m_calculated_value;
}


bool MyClass::IsValueReady() {

    return m_value_ready;
}

// Gets called from an std::thread or worker thread
void MyClass::ThreadFunctionToCalculateValue() {

    std::unique_lock<std::mutex> lock(m_mutex);

    m_calculated_value = ComplexLogicToCalculateValue();
    m_value_ready = true;

    m_my_condition_variable.notify_one();
}

问题:
但是问题是m_my_condition_variable.wait永远不会返回。

题:
我在这里做错了什么?

使UI线程等待来自工作线程的条件变量信号是否正确? 我如何摆脱由于工作线程函数错误而导致condition_variable永远不会触发的情况? 有什么办法可以在这里使用超时吗?

试图了解其工作原理:
我在许多示例中看​​到,他们使用while循环检查 condition_var.wait周围的布尔变量condition_var.wait 变量的循环点是什么? 广东话我希望 m_my_condition_variable返回出waitnotify_one从其他线程调用?

最有可能发生的事情:您的工作线程拥有并持有该互斥锁,直到完成计算为止。 主线程必须等待,直到它可以获取锁为止。 it releases the lock (in the destructor), by which time no other thread that would want to wait on the condition variable could have been acquired the lock that it still occupied by the notifying thread. 工作者将释放锁定 (在析构函数中)向CV发送信号,届时将没有其他想要等待条件变量的线程获得通知线程仍在占用的锁定。 因此,另一个线程在获得通知时永远没有机会等待条件变量,因为它在通知事件发生后才设法获取了锁定,从而导致它无限等待。

解决方案是删除MyClass :: ThreadFunctionToCalculateValue()中的锁定获取,根本不需要,或者至少不需要这样做。

但是无论如何,为什么要重新发明轮子呢? 对于此类问题,已创建std :: future

auto future = std::async(std::launch::async, ComplexLogicToCalculateValue);
bool is_ready = future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
auto result = future.get();

在这里,您可以轻松定义超时,而不必担心condition_variables之类的问题。

不能我希望当从其他线程调用notify_one时m_my_condition_variable返回等待状态吗?

,不是唯一的。 仍然可能发生虚假唤醒。

在这里看看这个例子:

http://en.cppreference.com/w/cpp/thread/condition_variable

下面的示例代码的注释中指出的对相关代码的更改。 您可能要考虑使用与cppreference.com示例中使用的“握手”相同的方式来在安全地计算新值(UI线程具有wait / notify,工作线程具有notify / wait)时进行同步。

在条件变量等待之前,需要锁定该锁。 等待将解锁,等待通知,然后锁定并使用谓词功能,检查是否准备就绪,如果尚未准备就绪(伪唤醒),请重复该循环。

在notify_one之前,应先解锁该锁,否则将唤醒等待,但无法获取锁(因为它仍处于锁定状态)。

std::mutex              m_mutex;
std::condition_variable m_my_condition_variable;
bool                    m_value_ready = false;  // init to false
unsigned int            m_calculated_value;


// Gets called from UI thread
unsigned int MyClass::GetCalculatedValue() {
    std::unique_lock<std::mutex> lock(m_mutex);
    m_my_condition_variable.wait(lock, std::bind(&MyClass::IsValueReady, this));
    m_value_ready = false;    // don't change until after wait
    return m_calculated_value;
}  // auto unlock after leaving function scope

bool MyClass::IsValueReady() {

    return m_value_ready;
}

// Gets called from an std::thread or worker thread
void MyClass::ThreadFunctionToCalculateValue() {
    std::unique_lock<std::mutex> lock(m_mutex);
    m_calculated_value = ComplexLogicToCalculateValue();
    m_value_ready = true;
    lock.unlock();         // unlock before notify
    m_my_condition_variable.notify_one();
}

或替代:

// Gets called from an std::thread or worker thread
void MyClass::ThreadFunctionToCalculateValue() {

    {   // auto unlock after leaving block scope
        std::lock_guard<std::mutex> lock(m_mutex);
        m_calculated_value = ComplexLogicToCalculateValue();
        m_value_ready = true;
    }   // unlock occurs here
    m_my_condition_variable.notify_one();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM