C++11——多线程编程7
翻译来自:https://thispointer.com//c11-multithreading-part-7-condition-variables-explained/
条件变量是一种用于在2个线程之间进行信令的事件,一个线程可以等待它得到信号,其他的线程可以给它发信号。
在c++11中,条件变量需要头文件:
#include <condition_variable>
同时,条件变量还需要一个mutex锁
条件变量实际上是如何工作的,
- 线程 1 调用等待条件变量,该变量在内部获取互斥锁并检查是否满足所需条件。
- 如果不是,则它释放锁并等待条件变量收到信号(线程被阻塞)。条件变量的 wait() 函数以原子方式提供这两种操作。
- 另一个线程,即线程 2,在满足条件时向条件变量发出信号
- 一旦条件变量得到信号,等待它的线程 1 就会恢复。然后它再次获取互斥锁并检查与条件变量关联的条件是否实际满足或者是否是上级调用。如果有多个线程在等待,notify_one 将只解除一个线程的阻塞。
- 如果是上级调用,则它再次调用 wait() 函数。
条件变量的主要成员函数是:
Wait()
他使得当前进程阻塞 知道条件变量得到信号或者是被虚假唤醒
它原子性地释放附加的mutex,阻塞当前线程,并将其添加到等待当前条件变量对象的线程列表中,当某线程在同样的条件变量上调用notify_one() 或者 notify_all(),线程将被解除阻塞;
这种行为也可能是虚假的,因此,解除阻塞后,需要再次检查条件;
一个回调函数会传给该函数,调用它来检查其是否是虚假调用,还是确实满足了真实条件;
当线程解除阻塞后,wait()函数获取mutex锁,并检查条件是否满足,如果条件不满足,则再次原子性地释放附加的mutex,阻塞当前线程,并将其添加到等待当前条件变量对象的线程列表中。
notify_one()
如果所有线程都在等待相同的条件变量对象,那么notify_one会取消阻塞其中一个等待线程。
notify_all()
如果所有线程都在等待相同的条件变量对象,那么notify_all会取消阻塞所有的等待线程。
如何处理第六节讨论的带有条件变量的多线程情景呢?
code:
#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() { //使该线程sleep 1秒 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "Loading Data from XML" << std::endl; //锁定数据 std::lock_guard<std::mutex> guard(m_mutex); //flag设为true,表明数据已加载 m_bDataLoaded = true; //通知条件变量 m_condVar.notify_one(); } bool isDataLoaded() { return m_bDataLoaded; } void mainTask() { std::cout << "Do some handshaking" << std::endl; //获取锁 std::unique_lock<std::mutex> mlock(m_mutex); //开始等待条件变量得到信号 //wait()将在内部释放锁,并使线程阻塞 //一旦条件变量发出信号,则恢复线程并再次获取锁 //然后检测条件是否满足,如果条件满足,则继续,否则再次进入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; }