2017-10-07 256 views
0

我试图使用C++11std::condition_variable来处理UI线程&工作者线程之间的数据事务。C++:如何在UI线程和工作者std :: thread之间使用std :: condition_variable

情况:
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周围的布尔变量的状态。什么是变量的循环点? 当我从其他线程调用notify_one时,我能期待m_my_condition_variable返回wait吗?

+0

你指的这些例子并没有使用'等待()'与谓词过载。因此他们基本上重新实现了那一个。无论如何,对于你的用例,你应该使用未来,而不是条件变量。 – Ext3h

+0

当MyClass :: GetCalculatedValue返回时,我需要'm_calculated_value'准备好所需的值。这看起来像线程之间的一种典型信号情况,告诉数据已准备就绪。 @ Wxt3h你不这么认为吗?未来将如何帮助这里?你能再详细一点吗? –

+0

我发现许多错误,但不会导致你的症状。您提出了多个问题,但未能提供[mcve]。有关于如何使用条件变量的SO Q&A,我建议阅读这些内容并按照他们的说法做。 – Yakk

回答

0

在这里这个例子看看:

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

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

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

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

什么是最有可能发生的: 您的工作线程拥有并持有互斥量,直到完成计算。主线程必须等到它可以获取锁定。工人将传达出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退出等待状态吗?

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

+0

好的。那么我不应该在这里使用condition_variable。感谢您的建议 –

+0

我认为可以处理虚假唤醒?如果一个'condition_variable'至少可以用于这个场景,那么我可以尝试处理虚假唤醒。 –

+0

当然可以。但为什么麻烦的是,如果手头有更清洁的解决方案呢? – Jodocus

相关问题