翻译来自:C++11 Multithreading – Part 7: Condition Variables Explained
在本文中,我们将通过示例讨论C ++ 11多线程中条件变量的用法。
**
条件变量
**
条件变量是一种事件,用于在两个或多个线程之间进行信号传递。一个或多个线程可以等待它发出信号,而另一个线程可以发出信号。
C ++ 11中的条件变量所需的头文件是,
#include <condition_variable>
需要互斥锁以及条件变量。
条件变量如何实际起作用,
线程1调用wait on condition变量,该变量在内部获取互斥量并检查是否满足所需条件。
如果不是,则释放锁定并等待条件变量得到信号(线程被阻塞)。条件变量的wait()函数以原子方式提供这两种操作。
当满足条件时,另一个线程(如线程2)会发出条件变量的信号
一旦条件变量得到信号,正在等待它的线程1恢复。然后,它再次获取互斥锁,并检查与条件变量关联的条件是否真正满足或是否为上级调用。如果有多个线程在等待,那么notify_one将仅解除阻塞一个线程。
如果是上级调用,则再次调用wait()函数。
std :: condition_variable的主要成员函数是:
Wait()
它使当前线程阻塞,直到信号通知条件变量或发生虚假唤醒为止。
它以原子方式释放附加的互斥锁,阻塞当前线程,并将其添加到等待当前条件变量对象的线程列表中。当某些线程在同一条件变量对象上调用notify_one()或notify_all()时,该线程将被解除阻塞。它也可能会被虚假地解除阻塞,因此,每次解除阻塞后,都需要再次检查条件。
回调将作为参数传递给此函数,然后将调用该回调以检查它是否是虚假调用或是否实际满足条件。
线程解锁后,
wait()函数重新获取互斥锁,并检查是否满足实际条件。如果不满足条件,那么它将再次自动释放附加的互斥锁,阻塞当前线程,并将其添加到等待当前条件变量对象的线程列表中。
notify_one()
如果有任何线程在同一条件变量对象上等待,则notify_one解除阻塞其中一个等待线程。
notify_all()
如果有任何线程在同一条件变量对象上等待,则notify_all取消阻止所有正在等待的线程。
让我们看看如何使用条件变量(即条件变量)处理先前讨论的多线程方案
多线程中需要事件处理 (在本文中,我们没有事件处理就解决了问题)
问题场景
假设我们正在构建一个基于网络的应用程序。此应用程序执行以下任务,
- 与服务器进行一些握手
- 从XML文件加载数据
- 对从XML加载的数据进行处理。
如我们所见,任务1不依赖于任何其他任务,但是任务3依赖于任务2。因此,这意味着任务1和任务2可以由不同的线程并行运行以提高应用程序的性能。因此,让我们将其分解为一个多线程应用程序,
线程1的职责是
- 与服务器进行一些握手。
- 等待线程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()
{
// 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()
{
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_2.join();
thread_1.join();
return 0;
}