C++11多线程之条件变量

news2024/11/24 9:59:22

文章目录

  • 一、关于多线程的同步
  • 二、初始条件变量
  • 三、关于条件变量的例题
  • 四、生产者消费者模型

一、关于多线程的同步

//函数被调用,分配相应的栈帧,进行现场保护

void func(char c)
{
	char filename[20] = {};
	sprintf(filename, "test%c.txt",c);
	FILE *fp=fopen(filename, "w");
	for (int i = 0; i < 10; ++i)
	{
		for (int j = 0; j < 10; ++j)
		{
			fprintf(fp,"%c ",c);
		}
		fprintf(fp,"\n");
	}
	fprintf(fp,"\n");
}

int main()
{
	thread tha(func, 'A');
	thread thb(func, 'B');
	thread thc(func, 'C');
	thread thd(func, 'D');
	thread the(func, 'E');

	tha.join();
	thb.join();
	thc.join();
	thd.join();
	the.join();
}

打印结果,每个文件里面分别打印出ABCDE
在这里插入图片描述

二、初始条件变量

关于条件变量的API函数
在这里插入图片描述


std::mutex m_cv;
std::condition_variable cv;
std::string mydata;
bool ready = false;
bool processed = false;

void worker_thread()
{
	std::unique_lock<std::mutex>lock(m_cv);  //构造函数,获得锁,块作用域结束后,调用析构函数,释放锁

	while (!ready)
	{
		cv.wait(lock);  //1.解锁 2.放在条件变量等待队列中 3.唤醒条件变量,放入互斥锁等待队列中 4.互斥锁到就绪状态,再次获得锁
	}
	mydata+= "处理数据完成";
	cout << mydata << endl;
	processed = true;
	//lock.unlock();
	cv.notify_one();
}

int main()
{
	std::thread tha(worker_thread);
	mydata += "data";  //块作用域
	{
		std::unique_lock<std::mutex>lock(m_cv);
		ready = true;
		cout << "main() signale data ready for processing" << endl;
	}
	cv.notify_one();
	{
		std::unique_lock<std::mutex>lock(m_cv);
		while (!processed)
		{
			cv.wait(lock);
		}
	}
	cout << "back int main() , data" << endl;
	tha.join();
	return 0;
}

在这个程序里面两个主线程和子线程同时运行,看谁先获得锁,如果子线程先获得锁,则进入wait状态,在wait状态里面有四个步骤

  • 释放锁
  • 将数据放入条件变量等待对列中
  • 唤醒条件变量,加入到互斥锁队列
  • 互斥锁到就绪状态,再次获得锁

当wait状态执行到第二步时候,此时主线程获得锁,主线程向下执行,将tag的值变为true,此时再次到子线程,此时子线程向下执行,wait执行三、四步骤后,再次调用唤醒函数,主线程执行后续步骤
在这里插入图片描述

三、关于条件变量的例题

例题一:ABC三个线程分别打印出来ABC

const int n = 10;
int tag = 1;
std::mutex mtx;
std::condition_variable cv;

void funa()
{
	std::unique_lock<std::mutex>lock(mtx);
	for (int i = 0; i < n; ++i)
	{
		while (tag != 1)
		{
			cv.wait(lock);
		}
		printf("funa :%c\n", 'A');
		tag = 2;
		cv.notify_one();
	}
}

void funb()
{
	std::unique_lock<std::mutex>lock(mtx);
	for (int i = 0; i < n; ++i)
	{
		while (tag != 2)
		{
			cv.wait(lock);
		}
		printf("funb :%c\n", 'B');
		tag = 3;
		cv.notify_one();
	}
}

void func()
{
	std::unique_lock<std::mutex>lock(mtx);
	for (int i = 0; i < n; ++i)
	{
		while (tag != 3)
		{
			cv.wait(lock);
		}
		printf("func :%c\n", 'C');
		tag = 1;
		cv.notify_one();
	}
}

int main()
{
	std::thread tha(funa);
	std::thread thb(funb);
	std::thread thc(func);

	tha.join();
	thb.join();
	thc.join();
}

运行结果:
在这里插入图片描述
在这里插入图片描述

例题二:ABC三个线程分别打印出100之内所有数

const int n = 100;
int i = 0;
std::mutex mtx;
std::condition_variable cv;

void funa()
{
	std::unique_lock<std::mutex>lock(mtx);
	while (i <= n)
	{
		while (i%3!=0&&i<=n)
		{
			cv.wait(lock);
		}
		if (i > 100)return;
		printf("funa:%d\n", i);
		i += 1;
		cv.notify_all();
	}
}
void funb()
{
	std::unique_lock<std::mutex>lock(mtx);
	while (i <= n)
	{
		while (i%3!= 1&&i<=n)
		{
			cv.wait(lock);
		}
		if (i > 100)return;
		printf("funb:%d\n", i);
		i += 1;
		cv.notify_all();
	}

}
void func()
{
	std::unique_lock<std::mutex>lock(mtx);
	while (i <= n)
	{
		while (i%3 != 2&&i<=n)
		{
			cv.wait(lock);
		}
		if (i > 100)return;
		printf("func:%d\n", i);
		i += 1;
		cv.notify_all();
	}
}

int main()
{
	thread tha(funa);
	thread thb(funb);
	thread thc(func);

	tha.join();
	thb.join();
	thc.join();
}

在这里插入图片描述
例题三:A线程打印偶数,B线程打印奇数

const int n = 100;
int i = 0;
bool tag = true;  //false 奇数
std::mutex mtx;
std::condition_variable cv;


void funa()
{
	std::unique_lock<std::mutex>lock(mtx);
	for (; i <=n; )
	{
		/*while (i % 2 != 0 && i <= n)
		{
			cv.wait(lock);
		}*/
		cv.wait(lock, [&]()->bool {return !(i % 2 != 0 && i <= n); }); //先判断lamanda表达式是否为真,如果为真,则直接退出
		if (i <=n)
		{
			printf("funa: %d\n", i);
		}
		i += 1;
		cv.notify_one();
	}
}
void funb()
{
	std::unique_lock<std::mutex>lock(mtx);
	for (; i <= n; )
	{
		while (i % 2 == 0 && i <= n)
		{
			cv.wait(lock);
		}
		if (i <= n)
		{
			printf("funb: %d\n", i);
		}
		i += 1;
		cv.notify_one();
	}
}

int main()
{
	thread tha(funa);
	thread thb(funb);

	tha.join();
	thb.join();
}

在这里插入图片描述

四、生产者消费者模型

//生产者消费者模型
const int n = 100;
int i = 0;
int mydata;
bool ready = false;
std::mutex mtx;  
bool stop = true;  //拓展,消费者不管生产者生产了多少数据,消费者始终进行消费
std::condition_variable cv;

void Produce()
{
	std::unique_lock<std::mutex>lock(mtx);
	for (int i=0; i <=n;++i)
	{
		while (ready)
		{
			cv.wait(lock);
		}
	
		mydata = i;
		printf("生产者生产数据:%d\n", mydata);
		ready = true;
		cv.notify_all();
	}
	stop = false;
	printf("生产者结束\n");
}

void Consumer1()
{
	std::unique_lock<std::mutex>lock(mtx);
		
	while(stop)
	{
		while (!ready)
		{
			cv.wait(lock);
		}
		printf("消费者一消费数据:%d\n", mydata);
		ready = false;
		cv.notify_all();
	}
}

void Consumer2()
{
	std::unique_lock<std::mutex>lock(mtx);

	while (stop)
	{
		while (!ready)
		{
			cv.wait(lock);
		}
		printf("消费者二消费数据:%d\n", mydata);
		ready = false;
		cv.notify_all();
	}
}

int main()
{
	thread tha(Produce);
	thread thb(Consumer1);
	thread thc(Consumer2);

	tha.join();
	thb.join();
	thc.join();
	return 0;
}

使用队列解决生产者消费者模型

//队列实现生产者和消费者
const int n = 100;
class MyQueue
{
private:
	std::deque<int>qu;
	std::mutex m_cv;
	std::condition_variable cv;
	std::size_t MaxElem = 8;
public:
	MyQueue(){}
	~MyQueue(){}
	MyQueue(const MyQueue&) = delete; //C++11新特性
	MyQueue& operator=(const MyQueue&) = delete;
	void put(int val)
	{
		std::unique_lock<std::mutex>lock(m_cv);
		while (qu.size() >= MaxElem)
		{
			cv.wait(lock);

		}
		qu.push_back(val);
		printf("Produce data:%d\n", val);
		cv.notify_one(); //唤醒
	}
	int get()
	{
		std::unique_lock<std::mutex>lock(m_cv);
		while (qu.empty())
		{
			cv.wait(lock);
		}
		int val = qu.front();
		qu.pop_front();
		cv.notify_all();
		return val;
	}
};
void Produre(MyQueue& qu)
{
	for (int i = 1; i <= n; ++i)
	{
		qu.put(i);
	}
}
void Consumer(MyQueue& qu)
{
	for (int i = 1; i <= n; ++i)
	{
		int x = qu.get();
		cout << "Consumer: " << x << endl;
	}
}
int main()
{
	MyQueue mqu;
	std:: thread tha(Produre,std::ref(mqu));
	std::thread thb(Consumer,std::ref(mqu));

	tha.join();
	thb.join();
}

出现死锁的情况

//死锁,解决死锁的方式,同时获取锁,同时释放锁
std::mutex mx1;
std::mutex mx2;
void funa()
{
	cout << "funa begin..." << endl;
	std::unique_lock<std::mutex>lock1(mx1);
	cout << "funa 拿到mx1" << endl;
	cout << "wait 拿到mx2" << endl;
	std::unique_lock<std::mutex>lock2(mx2);
	cout << "funa end..." << endl;
}
void funb()
{
	cout << "funb begin..." << endl;
	std::unique_lock<std::mutex>lock2(mx2);
	cout << "funb 拿到mx2" << endl;
	cout << "wait 拿到mx1" << endl;
	std::unique_lock<std::mutex>lock1(mx1);
	cout << "funb end..." << endl;
}
int main()
{
	std::thread tha(funa);
	std::thread thb(funb);

	tha.join();
	thb.join();
}

运行结果:
在这里插入图片描述
原因分析:并不是一运行就会出现这种情况,当出现图片中的这种情况的时候,原因是当两个互斥锁同时运行时,锁一被占用后,还没有释放锁的时候,锁二就继续占用锁一,出现了相互引用的情况,导致了死锁的出现。
解决死锁的办法
在这里插入图片描述
在这里插入图片描述
重写此程序:

std::mutex mx1;
std::mutex mx2;
void funa()
{
	cout << "funa begin..." << endl;
	std::unique_lock<std::mutex>lock1(mx1,std::defer_lock); //不获得互斥锁的所有权
	std::unique_lock<std::mutex>lock2(mx2, std::defer_lock);
	std::lock(lock1, lock2);  //将锁同时锁住,在将锁同时释放
	cout << "funa 拿到mx1" << endl;
	cout << "wait 拿到mx2" << endl;
	cout << "funa end..." << endl;
}
void funb()
{
	cout << "funb begin..." << endl;
	std::unique_lock<std::mutex>lock2(mx2, std::defer_lock);
	std::unique_lock<std::mutex>lock1(mx1, std::defer_lock);
	std::lock(lock2, lock1);
	cout << "funb 拿到mx2" << endl;
	cout << "wait 拿到mx1" << endl;
	cout << "funb end..." << endl;
}
int main()
{
	std::thread tha(funa);
	std::thread thb(funb);

	tha.join();
	thb.

在这里插入图片描述
关于银行转账死锁的问题
甲向乙进行转账的同时,乙向甲进行转账

#include <mutex>
#include <iostream>
#include <thread>
#include <condition_variable>
#include <set>
#include <unordered_set>
using namespace std;
mutex mx;
condition_variable cv;

class Count
{
public:
 Count(int m) : money(m) {}
 ~Count() = default;
 bool operator<(const Count& src)const { return money < src.money; }
 int getMoney() { return money; }
 void setMoney(int m) { money = m; }
private:
 int money;
};

class Account
{
private:
 Account() = default;
 Account(const Account & src) = delete;
 Account& operator=(const Account & src) = delete;
 set<Count*> s;
public:
 ~Account() = default;
 static Account* getInstance()
 {
  static Account a;
  return &a;
 }
 void apply(Count& A, Count& B)
 {
  unique_lock<mutex> lc(mx); //申请账户 先上锁
  while (s.count(&A) > 0 || s.count(&B) > 0)
  {
   cv.wait(lc); //阻塞等待
  }
  s.insert(&A);
  s.insert(&B);
 }
 void free(Count& A, Count& B)
 {
  unique_lock<mutex> lc(mx);
  s.erase(&A);
  s.erase(&B);
  cv.notify_all();
 }

};

void thread_func1(Count& A, Count& B, int money) //A---> B转账money元
{
 Account* acc = Account::getInstance();
 acc->apply(A, B);

 if (A.getMoney() >= money)
 {
  A.setMoney(A.getMoney() - money);
  B.setMoney(B.getMoney() + money);
  cout << "successful:" << endl;
  cout << money << endl;
 }
 else
 {
  cout << "余额不足" << endl;
 }
 acc->free(A, B);
}

int main()
{
 Count A(1000);
 Count B(1000);
 thread s1(thread_func1, ref(A), ref(B), 300); //A----->B 300
 thread s2(thread_func1, ref(B), ref(A), 100); //B----->A 100
 s1.join();
 s2.join();
 cout << A.getMoney() << endl;
 cout << B.getMoney() << endl;
 system("pause");
 return 0;
}

用户态到内核态怎么切换
http://t.csdn.cn/H7PAH
两个线程怎么切换
http://t.csdn.cn/O0oFb
死锁产生的原因和解决办法
http://t.csdn.cn/1AK4y

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/650509.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fiddler Response私人订制

在客户端接口的测试中&#xff0c;我们经常会需要模拟各种返回状态或者特定的返回值&#xff0c;常见的是用Fiddler模拟各种请求返回值场景&#xff0c;如重定向AutoResponder、请求拦截修改再下发等等。小编在近期的测试中遇到的一些特殊的请求返回模拟的测试场景&#xff0c;…

《变形金刚7》票房大跳水!特效敷衍?剧情单薄?汽车人的未来在哪里?

《变形金刚&#xff1a;超能勇士崛起》 6.11&#xff08;上映第3天&#xff09; 单日票房8200万 6.12&#xff08;上映第4天&#xff09; 单日票房2173万 6.13&#xff08;上映第5天&#xff09; 单日票房1700万 说实在的&#xff0c;真没想到《变形金刚7》日票房会如此大幅…

高压放大器在铁电材料中的应用研究

铁电材料是一种具有特殊磁电性能的材料&#xff0c;包括压电陶瓷、磷酸铁钠陶瓷、氧化锌压电陶瓷等。这些材料在电力、电子、机械等领域有广泛的应用&#xff0c;如超声波发生器、声纳、压力传感等。其中&#xff0c;高压放大器在铁电材料中有着重要的应用。 一、高压放大器的基…

完美搭建一个vue3+ts项目(一篇文章搞定你的所有疑惑)

目录 一、创建vite项目 二、启动vite项目 三、处理一些配置问题 四、增加工程化插件 1、安装sass 2、安装vue-router 3、安装pinia 4、安装element-plus 5、安装axios 6、设置路径别名&#xff0c;将相对路径改为绝对路径 一、创建vite项目 1、在一个文件夹下通…

Vision Pro:为什么空间音频是AR的绝杀武器?

Apple Vision Pro&#xff0c;不仅仅是苹果全新的重磅品类&#xff0c;而且在它身上也融合了苹果过去几乎所有新技术&#xff0c;比如空间音频就是其中一个例子。 苹果表示&#xff0c;Vision Pro中空间音频可以很好的应用在&#xff1a;影视节目、游戏内容、3D空间照片、3D空…

金融风控项目实战-银行信用卡流失预测模型_基于ANN神经网络_金融培训_论文科研_毕业设计

业务背景 根据央行公布的数据显示&#xff0c;全国性银行信用卡和借贷合一卡的发卡量增速从2017年同比增速26.35%的高点逐年下降&#xff0c;截至2020年同比增速降至4.26%。银行信用卡发卡增速明显放缓的背景下&#xff0c;预防老客户流失的问题变得愈发重要。 假设一家消费信…

pytest+allure

知识点1&#xff1a; 1、测试结果信息阅读 passed表示通过&#xff0c;有个简写. failed表示失败&#xff0c;有个简写F 2、命令行参数 -h&#xff1a;帮助 -version&#xff1a;版本信息 3、测试用例命名规则&#xff1a; 测试函数必须以test开头 测试类必须以Test开头…

我为开放原子全球开源峰会助力:共建开源之梦

我为开放原子全球开源峰会助力&#xff1a;共建开源之梦 6月11日&#xff0c;以“开源赋能&#xff0c;普惠未来”为主题的2023开放原子全球开源峰会开幕式暨高峰论坛在北京成功举办。 开源的力量与魅力 开源是当今软件行业中不可忽视的力量&#xff0c;它为技术的快速发展和…

入职滴滴和字节的2 年里,我感觉忒真实了……

引言 先简单交代一下背景吧&#xff0c;某不知名985的本硕&#xff0c;17年毕业加入滴滴&#xff0c;之后跳槽到了头条&#xff0c;一直从事软件测试相关的工作。之前没有实习经历&#xff0c;算是两年半的工作经验吧。 这两年半之间完成了一次晋升&#xff0c;换了一家公司&…

Ubuntu如何安装vmtools?

虚拟机-安装VMware Tools 然后在Ubuntu中找到设备中的VMware Tools&#xff0c;将这个文件来复制到桌面上去。 选择提取到此处 可以看到桌面上多了一个VMware Tools的文件夹 使用cd命令进入桌面上的这个VMware tools的文件夹 使用sudo ./安装命令 对vmware-tools-distrib文件夹…

政企市场,「观望」AI大模型

数据安全等“刚需”下&#xff0c;私有化成为政企市场的准入门槛&#xff0c;然而私有化下&#xff0c;行业模型局限性尚未可知&#xff0c;加之信创化等因素&#xff0c;厂商仍需取长补短&#xff0c;为政企客户提供全方位的解决方案。 作者|斗斗 编辑|皮爷 出品|产业家 政…

采用UWB定位技术开发的室内定位系统源码

UWB精准定位系统源码 UWB是什么&#xff1f; UWB(Ultra Wideband)超宽带技术是一种全新的、与传统通信技术有极大差异的通信新技术。它不需要使用传统通信体制中的载波&#xff0c;而是通过发送和接收具有纳秒或纳秒级以下的极窄脉冲来传输数据&#xff0c;实现精准定位。 技术…

一名高水平的程序员,为什么面试总是失败?

目录 场景1 场景2 场景3 1 从发现和分析问题方面准备。 2 从linux命令和日志层面分析问题的步骤。 1 事先准备很重要 2 面试开始是自我介绍&#xff0c;这时要表露出&#xff0c;自己解决过redis,dubbo等组件的线上问题 3 一旦开始说第一个组件层面解决过的问题时&…

Django高级扩展之文件上传

文件上传是一个比较常用的网站功能&#xff0c;在服务器端&#xff0c;Django会使用一个叫作request.FILES的对象来处理上传的文件。 目录 存储路径 创建存储目录 配置settings.py 上传单文件 配置url 上传文件模板 视图方法 显示上传页面 上传文件处理 上传效果 1.…

盘点五种最常见加密算法!

大家好&#xff0c;我是Martin。 今天&#xff0c;就给大家来盘点一下最常见的5种加密算法。 大家平时的工作中&#xff0c;可能也在很多地方用到了加密、解密&#xff0c;比如&#xff1a; 用户的密码不能明文存储&#xff0c;要存储加密后的密文 用户的银行卡号、身份证号…

2024年天津农学院专升本专业课参考书目

天津农学院2024年高职升本科拟招生专业参考书目 天津农学院专升本专业课参考教材&#xff08;我校不提供专业课参考教材&#xff09; 人力资源管理专业参考教材&#xff1a; 1、《人力资源管理实用教程》 (第2版)&#xff0c;吴宝华&#xff0c;北京大学出版社 2、《人力资源…

运维(SRE)成长之路-第3天 文本处理三剑客之 grep

1.命令作用以及参数详解 grep: 全局搜索正则表达式并打印行(Global search REgular expression and Print out the line)作用&#xff1a;文本搜索工具&#xff0c;根据用户指定的“模式”对目标文本逐行进行匹配检查&#xff1b;打印匹配到的行模式&#xff1a;由正则表达式字…

GitHub Copilot 最全安装、使用

GitHub Copilot 最全安装、使用教程 一、温馨提示 GitHub Copilot 目前为止可以免费试用一个月&#xff0c;但是试用的前提是必须要绑定银行卡&#xff0c;因为后续会自动扣费&#xff0c;所以请注意试用结束日期&#xff0c;自己定好闹钟关闭订阅。 订阅价格为每月10美刀&a…

【Pinia 状态管理篇】Vite + Vue3 组合式 API 模式下的 Store 数据处理

文章目录 Pinia 状态管理一、 Pinia 安装与使用1.1 安装1.2 注册 pinia 实例到全局1.3 创建一个 Store1.4 组件内使用 Store 二、Pinia 核心概念展开学习Store 的定义和使用2.1 State2.2 Getter2.3 Action 附&#xff1a;1. 什么是魔法字符串&#xff1f; Pinia 状态管理 一、 …

Nacos配置中心使用(Spring Cloud版)

目标 向项目中集成Nacos配置。原项目是一个SpringBoot项目。这里假设我们无法修改原有项目的SpringBoot版本。 注意 在不动SpringBoot版本的前提下&#xff0c;根据SpringBoot的版本&#xff0c;确定Spring Cloud和Nacos版本。Nacos版本其实就是Spring Cloud Alibaba版本。在…