条件变量->允许一个线程阻塞,直到另一个线程设置了某个条件或系统时间达到了某个指定时间。条件变量允许显示的线程间通信。
使用条件变量需要包含<condition_variable>,标准中有两种类型的条件变量。
- std::condition_variable: 只能等待unique_lock<mutex>的条件变量。
1 notify_once() 唤醒等待这个变量的线程之一。
2 notify_all() 唤醒所有等待这个变量的线程。
3 wait(unique_lock<mutex>&lk)
- std::condition_variable_any:可以等待任何对象的条件变量,包括自定义锁的类型
注:代码摘自 《c++高级编程》
<pre name="code" class="cpp">//logger.h
#pragma once
#include<string>
#include<thread>
#include<condition_variable>
#include<mutex>
#include<queue>
#include<fstream>
#include<iostream>
class logger
{
public:
//Starts a background thread writing log entries to a file
logger();
//Gracefully shut down background thread
virtual ~logger();
//Add log entry to the queue
void log(const std::string &entry);
protected:
void processEntries();
bool mThreadStarted;
bool mExit;
//Mutex and condition variable to protect when the thread starts executing its loop
//后台线程,处理队列
std::mutex mMutex;
std::condition_variable mCondvar;
//信息队列
std::queue<std::string> mQueue;
/*queue 的基本操作有:
入队,如例:q.push(x); 将x 接到队列的末端。
出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队首元素,如例:q.front(),即最早被压入队列的元素。
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
判断队列空,如例:q.empty(),当队列空时,返回true。*/
std::thread mThread;
std::mutex mMutexStarted;
std::condition_variable mCondVarStarted;
private:
//prevent copy construction and assignment
logger(const logger &src);
logger &operator =(const logger&rhs);
};
<div><pre name="code" class="cpp">//logger.cpp
#include "stdafx.h"
#include "logger.h"
//
logger::logger():mExit(false)
{
//Start background thread
mThread = std::thread{ &logger::processEntries,this};
//Wait until background thread starts its processing loop
std::unique_lock<std::mutex> lock(mMutexStarted);
//在mThreadStarted 为true是将线程唤醒
mCondVarStarted.wait(lock, [&]() {return mThreadStarted == true; });
}
logger::~logger()
{
//析构时将mExit 置为 true 并唤醒线程mThread 将队列中的信息全部写到log.txt 中 并将队列清空 并终止循环 避免程序结束时 队列中仍有信息
mExit = true;
mCondvar.notify_all();
//阻止调用线程。直到此实例的线程终止
// 防止队列中还有信息时 程序退出
mThread.join();
}
void logger::log(const std::string & entry)
{
std::unique_lock<std::mutex> lock(mMutex);
mQueue.push(entry);
mCondvar.notify_all();
}
void logger::processEntries()
{
//open file
std::ofstream ofs("log.txt");
if (ofs.fail())
{
std::cerr << "Faild to open logfile" << std::endl;
return;
}
//Star processing loop
//通过这个互斥体保护队列的访问
std::unique_lock<std::mutex> lock(mMutex);
// 将mThreadStarted 置为true 并唤醒mThread 防止后台线程还没有开始处理循环的时候 main函数剩下的代码都执行完了
mThreadStarted = true;
mCondVarStarted.notify_all();
while (true)
{
mCondvar.wait(lock);
lock.unlock();
while (true)
{
lock.lock();
if (mQueue.empty()) { break; }
else
{
//向log.txt中输出队列的首项,mQueue.front()返回队首元素
ofs << mQueue.front() << std::endl;
//出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
mQueue.pop();
}
lock.unlock();
}
if (mExit) break;
}
}
/*queue 的基本操作有:
入队,如例:q.push(x); 将x 接到队列的末端。
出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队首元素,如例:q.front(),即最早被压入队列的元素。
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
判断队列空, 如例:q.empty(),当队列空时,返回true。*/
#include "stdafx.h"
#include "logger.h"
#include <sstream>
void logSomeMessage(int id, logger &loge,std::string str)
{
std::stringstream ss;
ss << "ID " << id << " faild :" << str;
loge.log(ss.str());
}
int main()
{
logger _logger;
std::vector<std::thread> threads;
std::string str = "wrong";
/*for (int i = 0; i < 10; i++)
{
threads.push_back(std::thread{ logSomeMessage,i,std::ref(_logger),str });
}
for (auto &t : threads) {t.join();}*/
logSomeMessage(8456, _logger, "wrong");
return 0;
}