C++11条件变量condition_variable

前言

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

正文

条件变量只有五个函数:

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

条件变量的方法分为两种:通知等待,我们按照这个分类来说。
在这里插入图片描述

等待

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 <iostream>
#include <thread>
#include <memory>
#include <string>
#include <condition_variable>
#include <list>
#include <atomic>

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

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默示MoS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值