C++怎么实现两个线程交替打印

首先简单搭一个框架,让两个线程先尝试实现交替打印

//实现两个线程交替打印#include <iostream>#include <thread>using namespace std;int main(void){	int n = 100;	int i = 0;	//创建两个线程	thread t1([&n, &i](){		while (i < n)		{			cout << i << " ";			i++;		}	});	thread t2([&n, &i]() {		while (i < n)		{			cout << i << " ";			i++;		}	});	if (t1.joinable())	{		t1.join();	}	if (t2.joinable())	{		t2.join();	}	return 0;}

为了让我们更加清楚是哪个线程打印了,我们需要获取线程的ID。

#include <iostream>#include <thread>using namespace std;int main(void){	int n = 100;	int i = 0;	//创建两个线程	thread t1([&n, &i](){		while (i < n)		{			cout << this_thread::get_id()  << ": " << i << endl;			i++;		}	});	thread t2([&n, &i]() {		while (i < n)		{			cout << this_thread::get_id() << ": " << i << endl;			i++;		}	});	if (t1.joinable())	{		t1.join();	}	if (t2.joinable())	{		t2.join();	}	return 0;}

C++怎么实现两个线程交替打印

这显然没有完成两个线程交替打印的目的,甚至数据的打印都非常地乱。这是因为i是临界资源,多个线程争抢访问临界资源可能会造成数据二义,线程是不安全的,需要保证任意时刻只有一个线程能够访问临界资源。

所以创建一个互斥量,并在临界区合适的地方加锁和解锁。由于线程的执行函数我使用了lambda表达式,为了让两个线程使用的是同一把锁,把锁创建在了main函数内,并在lambda表达式内使用了引用捕捉。

#include <iostream>#include <thread>#include <mutex>using namespace std;int main(void){	int n = 100;	int i = 0;	mutex mtx;	//创建两个线程	thread t1([&n, &i, &mtx](){		while (i < n)		{			mtx.lock();			cout << this_thread::get_id()  << ": " << i << endl;			i++;			mtx.unlock();		}	});	thread t2([&n, &i, &mtx]() {		while (i < n)		{			mtx.lock();			cout << this_thread::get_id() << ": " << i << endl;			i++;			mtx.unlock();		}	});	if (t1.joinable())	{		t1.join();	}	if (t2.joinable())	{		t2.join();	}	return 0;}

在C++中,一般不直接操作锁,而是由类去管理锁。

//名列前茅个管理锁的类template <class Mutex> class lock_guard;//第二个管理锁的类template <class Mutex> class unique_lock;

lock_guar类,只有构造和析构函数。一般用于加锁和解锁,这里进行简单的模拟:

//注意:为了使得加锁和解锁的是同一把锁//需要使用引用template <class Lock>class LockGuard{public:	LockGuard(Lock &lck)		:_lock(lck)	{		_lock.lock();	}	~LockGuard()	{		_lock.unlock();	}private:	Lock &_lock;};

unique_lock的成员方法就不仅仅是析构函数和构造函数。详见文档unique_lock介绍和使用。

这里将锁交给unique_lock类的对象进行管理。

int main(void){	int n = 100;	int i = 0;	mutex mtx;	//创建两个线程	thread t1([&n, &i, &mtx, &cv, &flag](){		while (i < n)		{			unique_lock<mutex> LockManage(mtx);			cout << this_thread::get_id()  << ": " << i << endl;			i++;		}	});	thread t2([&n, &i, &mtx, &cv, &flag]() {		while (i < n)		{			unique_lock<mutex> LockManage(mtx);			cout << this_thread::get_id() << ": " << i << endl;			i++;		}	});	if (t1.joinable())	{		t1.join();	}	if (t2.joinable())	{		t2.join();	}	return 0;}

C++怎么实现两个线程交替打印

线程是安全了,但如果其中一个线程竞争锁的能力比较强,那么可能会出现上面这种情况。

需要控制:一个线程执行一次后,如果再次去执行就不准许了,同时可以唤醒另一个进程去执行,如此循环往复达到交替打印的目的。所以可以增加一个条件变量,让某个线程在该条件变量下的阻塞队列等待。

C++库中线程在条件变量下的等待函数名列前茅个参数注意是管理锁的类对象

int main(void){	int n = 100;	int i = 0;	mutex mtx;	condition_variable cv;	bool flag = false;	//创建两个线程	thread t1([&n, &i, &mtx, &cv, &flag](){		while (i < n)		{			unique_lock<mutex> LockManage(mtx);			//!flag为真,那么获取后不会阻塞,优先运行			cv.wait(LockManage, [&flag]() {return !flag; });			cout << this_thread::get_id()  << ": " << i << endl;			i++;		}	});	thread t2([&n, &i, &mtx, &cv, &flag]() {		while (i < n)		{			unique_lock<mutex> LockManage(mtx);			//flag为假,竞争到锁后,由于条件不满足,阻塞			cv.wait(LockManage, [&flag]() {return flag; });			cout << this_thread::get_id() << ": " << i << endl;			i++;		}	});	if (t1.joinable())	{		t1.join();	}	if (t2.joinable())	{		t2.join();	}	return 0;}

这里flag以及lambda表达式的增加是非常巧妙的。flag的初始化值为false,让线程t2在[&flag]() {return false; }下等待,那么t2线程就会先执行。

C++怎么实现两个线程交替打印

线程t1竞争到了锁,但是由于不满足条件,会继续等待,所以就出现了上面的情况。

需要一个线程唤醒另一个线程之前,将flag的值进行修改。

int main(void){	int n = 100;	int i = 0;	mutex mtx;	condition_variable cv;	bool flag = false;	//创建两个线程	thread t1([&n, &i, &mtx, &cv, &flag](){		while (i < n)		{			unique_lock<mutex> LockManage(mtx);			//!flag为真,那么获取后不会阻塞,优先运行			cv.wait(LockManage, [&flag]() {return !flag; });			cout << this_thread::get_id()  << ": " << i << endl;			i++;			flag = true;			cv.notify_one();		}	});	thread t2([&n, &i, &mtx, &cv, &flag]() {		while (i < n)		{			unique_lock<mutex> LockManage(mtx);			//flag为假,竞争到锁后,由于条件不满足,阻塞			cv.wait(LockManage, [&flag]() {return flag; });			cout << this_thread::get_id() << ": " << i << endl;			i++;			flag = false;			cv.notify_one();		}	});	if (t1.joinable())	{		t1.join();	}	if (t2.joinable())	{		t2.join();	}	return 0;}

最终,实现了两个线程交替打印(一个线程打印奇数、一个线程打印偶数)

以上就是关于“C++怎么实现两个线程交替打印”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。

文章标题:C++怎么实现两个线程交替打印,发布者:亿速云,转载请注明出处:https://worktile.com/kb/p/20984

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
亿速云的头像亿速云认证作者
上一篇 2022年8月27日 下午5:00
下一篇 2022年8月27日 下午5:01

相关推荐

  • css的含义有哪些

    css有多种含义:1、层叠样式表,是一种用来表现HTML或XML等文件样式的计算机语言;2、特卫安防集团,是国内少数定位高端安全服务的企业;3、集群交换机系统,是将几台交换机通过专用的集群线缆链接起来,对外呈现为一台逻辑交换机;4、内容扰乱系统,是一种防止直接从盘片上复制视频文件的数据加密和鉴定方法…

    2022年9月22日
    57000
  • Nmap运营的示例分析

    背景 随着安全行业的发展,国家的高度重视。各行业遭受着各种各样的威胁,甲方的一些企业自己没有相关的安全部门或者安全能力比较薄弱,就会聘请乙方的安全人员进行运营服务。那么乙方的安全工程师则需要帮助客户去处理一些业务运行过程当中出现的安全事件,比如:出现某个漏洞后需要我们安全工程师去检测一下其他的业务系…

    2022年9月16日
    45700
  • windows print spooler内存不能为read怎么解决

    问题描述 spoolsv.exe在打印文档的时候报错,内存不能为read! 问题分析 spoolsv.exe 是Print Spooler的进程,管理所有本地和网络打印队列及控制所有打印工作。如果此服务被停用,本地计算机上的打印将不可用。该进程属 Windows 系统服务。 spoolsv.exe用…

    2022年9月2日
    41600
  • linux能不能获取本地ip地址

    linux能获取本地ip地址。方法:1、利用“ifconfig -a”命令,该命令用于显示或者设置网络设备信息;2、利用“ip address”命令,语法为“ip address | grep eth0 | awk ‘{print$2}’”;3、利用“hostname -I”…

    2022年6月29日
    4.3K00
  • 怎么在html页面中调用外部样式

    两种调用方法:1、使用link标签调用,语法“<link href=”外部样式表文件路径” rel=”stylesheet” type=”text/css” />”;2、利用“@import”关键字调用,语法“&lt…

    2022年9月16日
    79700
  • 怎么修改pip install默认安装路径

    1. 修改pip install默认安装路径 一般使用Anaconda时会使用 pip install ### 来安装各类包,但默认安装路径在C盘,极大占用空间,作为强迫症,我们通过以下步骤来修改默认安装路径。 1.1 查看pip 默认安装位置 名列前茅步:通过win菜单,找到Prompt,点击进入…

    2022年8月30日
    3.4K00
  • mysql分页查询如何优化

    分页查询的优化方式:1、子查询优化,可通过把分页的SQL语句改写成子查询的方法获得性能上的提升。2、id限定优化,可以根据查询的页数和查询的记录数计算出查询的id的范围,然后根据“id between and”语句来查询。3、基于索引再排序进行优化,通过索引去找相关的数据地址,避免全表扫描。4、延迟…

    2022年9月24日
    2.1K00
  • jquery中子元素选择器和后代元素选择器有哪些区别

    jquery中子元素选择器和后代元素选择器的区别:1、子元素选择器的语法为“$(“父元素>子元素”)”,后代元素选择器的语法为“$(“父元素 子元素”)”;2、子元素选择器只对直接后代产生影响,而对多层后代不产生作用,而后代元素选择器是对所有指定…

    2022年9月2日
    48900
  • SQL Server怎么操作Json格式字段数据

    1 json存储 在sqlserver 中存储json ,需要用字符串类型进行存储,一般用nvarchar()或 varchar()进行存储,不要用text进行存储,用text时候,json的函数不支持。 2 json操作 主要介绍5个函数: (1)openJson:打开Json字符串 (2)IsJ…

    2022年8月29日
    1.0K00
  • windows驱动精灵usb驱动如何安装

    驱动精灵usb驱动安装方法: 1、打开进入驱动精灵点击“一键体检”。 2、等待扫描计算机。 3、扫描完成后点击“驱动程序”。 4、下拉找到USB,选择“下载”。 5、下载完成后点击“安装”。 6、等待安装即可。 7、最后点击“完成”即可完成安装。 读到这里,这篇“windows驱动精灵usb驱动如何…

    2022年9月13日
    50200
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部