C++11条件变量condition_variable

文章目录

    • 前言
    • 正文
      • 等待
      • 通知
      • 注意事项
    • 结尾

前言

条件变量用于多线程中,其作用是在多线程间实现线程的等待、唤醒和通知机制,常配合互斥锁(std::mutex)一起使用。它主要用于解决数据竞争问题>。

正文

条件变量只有五个函数:

方法 作用
notify_one() 通知一个等待的线程
notify_all() 通知所有等待的线程
wait() 阻塞该线程,直到条件变量被唤醒
wait_for() 阻塞该线程,直到条件变量被唤醒或者到达指定时限时长后
wait_until() 阻塞该线程,直到条件变量被唤醒或者到达指定时间点后

条件变量的方法分为两种:通知等待,我们按照这个分类来说。
C++11条件变量condition_variable_第1张图片

等待

wait()部分的就是等待函数,它接收两个参数:

template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

void wait(std::unique_lock<std::mutex>& lock);

它有两个版本,我们先说最简单的版本,它只有一个参数:

void wait(std::unique_lock<std::mutex>& lock);

它接收一个unique_lock作为参数。当程序运行到wait()这一行的时候,程序必定阻塞,只有等到通知之后才会继续运行,这个状态我们也称之为睡眠
那么有两个参数的呢?它的第二个参数是一个谓词,这里我们能够理解为一个函数,通常是使用lambda表达式。当wait()接到通知的时候,执行这个谓词,若是返回的结果为true,就获取锁的所有权,执行接下来的语句;若是为false,它就重新进入睡眠状态,继续阻塞线程,等待下一次通知的出现。
所以谓词的声明也等同于:

bool pred();

那么其他的wait就不多说了。

通知

通知有两个函数,notify_one()和notify_all(),前者只通知一个线程,而后者则会通知所有线程,在通知之后,被通知的线程会判断是否满足条件函数的要求,若是符合要求,则执行其后面的函数,若是不满足要求,则回到睡眠状态。

所以:条件变量的通知函数真的就是起到一个通知的作用,告诉因为wait()而阻塞的线程可以开始运行了,但是到底什么时候运行还要看实际情况

注意事项

notify_one() 和 notify_all() 的调用都不会立即执行实际的唤醒操作。相反,它们只是在条件变量上设置了一个唤醒标志,并在互斥锁释放之后,等待其他线程重新获取互斥锁时才会实际执行唤醒操作。
也就是说:只有能获取到互斥锁的时候才会进行唤醒,并让它去争抢互斥锁

结尾

条件变量的内容其实很少,也比较好理解,问题在怎么去使用它。
这里我给出一段代码,这段代码是一个简单的消息队列,也是个非常简单的生产者消费者队列,实现了一个消息的发送和接收功能,配合代码食用效果更佳(重要的地方我都写了注释,希望能够帮助大家理解):

#include 
#include 
#include 
#include 
#include 
#include 
#include 

std::mutex mtx;
std::condition_variable cv;
std::list<std::string> msg;

// 读取数据
void read_thread(){
	while(true){
		std::unique_lock<std::mutex> lock(mtx);

		// 阻塞等待消息(并且解锁)
		// 有消息再执行,没消息不执行
		cv.wait(lock,[&](){ return !msg.empty(); });
		// 获取到互斥锁

		std::cout << "收到消息,解析中:" << std::endl;

		// 将数据从队列中取出
		std::cout << msg.front() << std::endl;
		msg.pop_front();
	}
}

// 写入数据
void write_thread(){
	std::cout << "请输入需要发送的数据:" << std::endl;
	std::string input;
	while(true){
		if(std::cin >> input){
			std::unique_lock<std::mutex> lock(mtx);
			// 将数据放入队列
			msg.push_back(input);
			std::cout << "数据成功输入" << std::endl;

			// 通知read线程,有消息可以接收
			cv.notify_all();
		}
	}
}

int main(){
	std::thread write_(write_thread);
	// 后台运行
	write_.detach();
	
	std::thread read_(read_thread);
	// 后台运行
	read_.detach();

	// 阻塞主线程
	while(true);

	return 0;
}

这段代码只是一段小程序,它有很多不规范和不完美的地方。
我们还能使用原子操作atomic将程序进行优化,原子操作的相关知识可以看看我这篇文章:C++11原子操作atomic。

你可能感兴趣的:(玩转C++,c++,服务器)