C ++:如何在UI线程和&之间使用std :: condition_variable worker std :: thread

时间:2017-10-07 13:38:23

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

我正在尝试使用C++11中的std::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周围的布尔变量的状态。什么是变量的循环点? 当我从其他线程调用m_my_condition_variable时,我希望 wait退出notify_one吗?

2 个答案:

答案 0 :(得分: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,不仅限于此。仍然可能发生虚假的唤醒。

答案 1 :(得分:0)

在这里看一下这个例子:

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

以下示例代码中的注释中对相关代码的更改。您可能需要考虑使用与cppreference.com示例中使用的相同“握手”来同步,以便在计算新值时安全(UI线程具有等待/通知,工作线程具有通知/等待)。 / p>

在条件变量等待之前,需要锁定锁。等待将解锁,等待通知,然后锁定并使用谓词函数,检查是否准备好,如果没有准备好(虚假唤醒),重复循环。

在notify_one之前,锁应该被解锁,否则等待被唤醒,但是无法获得锁定(因为它仍然被锁定)。

Marker a,b;
GoogleMap mMap;
mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map))
    .getMap();
a= mMap.addMarker(new MarkerOptions()
    .position(sc)
    .title("A")
    .snippet("A")
    .icon(BitmapDescriptorFactory.fromResource(R.drawable.a)));
b= mMap.addMarker(new MarkerOptions()
    .position(lng)
    .title("B")
    .snippet("B")
    .icon(BitmapDescriptorFactory.fromResource(R.drawable.b)));
mMap.setOnMarkerClickListener(new OnMarkerClickListener()
{
    @Override
    public boolean onMarkerClick(Marker arg0) {
        // if marker source is clicked
        if(arg0.getTitle().equals("A")){
            // display toast
            Toast.makeText(MainActivity.this, arg0.getTitle(), Toast.LENGTH_SHORT).show();
        }
        // if marker source is clicked
        else if(arg0.getTitle().equals("B")){
            Toast.makeText(MainActivity.this, arg0.getTitle(), Toast.LENGTH_SHORT).show();
        }
        return true;
    }
});

或替代方案:

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