C++11 多线程 - Part 7:条件变量

翻译自:https://thispointer.com//c11-multithreading-part-7-condition-variables-explained/

在本文中,我们将通过示例讨论C++ 11多线程中条件变量的用法。

条件变量

条件变量是一种事件,用于在两个或多个线程之间进行信号传递。一个或多个线程可以等待它发出信号,而另一个线程可以发出信号。

C++ 11中的条件变量所需的头文件是

#include <condition_variable>

需要互斥锁以及条件变量。

条件变量实际如何工作

* 线程1调用条件等待变量,该变量在内部获取互斥量并检查是否满足所需条件。
* 如果没有满足条件,则释放锁并等待条件变量得到信号(线程被阻塞)。 条件变量的wait()函数以原子方式(?)提供这两种操作。
* 另一个线程,即像线程2一样在满足条件时发出条件变量信号
* 一旦条件变量得到信号,正在等待的线程1将恢复。 然后,它再次获取互斥锁,并检查与条件变量关联的条件是否实际满足或是否为上级调用。 如果有多个线程在等待,那么notify_one将仅解除一个线程。
* 如果是上级调用,则再次调用wait()函数。

std::condition_variable的主要成员函数是

Wait()

它使当前线程阻塞,直到信号通知条件变量或发生虚假唤醒为止。
它以原子方式释放附加的互斥锁,阻止当前线程,并将其添加到等待当前条件变量对象的线程列表中。 当某些线程在同一条件变量对象上调用notify_one()或notify_all()时,该线程将被解除阻塞。 它也可能会被虚假地解除阻塞,因此,每次解除阻塞后,都需要再次检查条件。
回调将作为参数传递给此函数,该函数将被调用以检查它是否是虚假调用或是否实际满足条件。
线程解锁后,
wait()函数重新获取互斥锁,并检查是否满足实际条件。 如果不满足条件,则再次自动释放附加的互斥锁,阻塞当前线程,并将其添加到等待当前条件变量对象的线程列表中。

notify_one()

如果有任何线程在同一条件变量对象上等待,则notify_one会解除封锁其中一个等待线程。

notify_all()

如果有任何线程在同一条件变量对象上等待,则notify_all解除封锁所有正在等待的线程。
让我们来看看如何使用条件变量

问题场景

假设我们正在构建一个基于网络的应用程序。 该应用程序执行以下任务,

1.与服务器进行一些handshaking
2.从XML文件加载数据。
3.对从XML加载的数据进行处理。

正如我们所见,任务1不依赖于任何其他任务,但是任务3依赖于任务2。因此,这意味着任务1和任务2可以由不同的线程并行运行以提高应用程序的性能。 因此,让我们将其分解为一个多线程应用程序,

线程1的职责是

* 与服务器进行一些handshaking。
* 等待线程2从XML加载数据
* 对从XML加载的数据进行处理。

线程2的职责是

* 从XML加载数据
* 通知另一个线程,即等待消息。

使用条件变量实现此目的的代码如下,

#include <iostream>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
using namespace std::placeholders;
class Application
{
    std::mutex m_mutex;
    std::condition_variable m_condVar;
    bool m_bDataLoaded;
public:
    Application()
    {
        m_bDataLoaded = false;
    }
    void loadData()
    {
        std::cout<<"loadData start"<<std::endl;
        // Make This Thread sleep for 1 Second
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout<<"Loading Data from XML"<<std::endl;
        // Lock The Data structure
        std::lock_guard<std::mutex> guard(m_mutex);
        // Set the flag to true, means data is loaded
        m_bDataLoaded = true;
        // Notify the condition variable
        m_condVar.notify_one();
  }
    bool isDataLoaded()
    {
        if (m_bDataLoaded) {
            std::cout << "load true" << std::endl;
        } else {
            std::cout << "load false" << std::endl;
        }
    return m_bDataLoaded;
  }
    void mainTask()
    {
        std::cout<<"Do Some Handshaking"<<std::endl;
        // Acquire the lock
        std::unique_lock<std::mutex> mlock(m_mutex);
        // Start waiting for the Condition Variable to get signaled
        // Wait() will internally release the lock and make the thread to block
        // As soon as condition variable get signaled, resume the thread and
        // again acquire the lock. Then check if condition is met or not
        // If condition is met then continue else again go in wait.
        m_condVar.wait(mlock, std::bind(&Application::isDataLoaded, this));

        std::cout<<"Do Processing On loaded Data"<<std::endl;
    }
};
int main()
{
    Application app;
    std::thread thread_1(&Application::mainTask, &app);
    std::thread thread_2(&Application::loadData, &app);
    thread_1.join();
    thread_2.join();

    return 0;
}

/*输出结果
Do Some Handshaking
load false
loadData start
Loading Data from XML
load true
Do Processing On loaded Data
*/
posted on 2020-04-22 15:29  JJ_S  阅读(408)  评论(0编辑  收藏  举报