【C++11】多线程+IO流

news2024/11/25 1:01:27


目录

一、C++11线程库

1、每个线程都有独立的栈空间

2、加锁的位置

3、CSA操作

4、C++的类模板atomic(原子操作)

5、lock_guard(RAII风格的锁)/unique_lock(可随时释放锁)

6、条件变量(用于互相通知)

二、IO流

1、C语言的输入输出缓冲区

2、C++流的概念

3、流提取

4、C++文件IO流

4.1二进制读写

4.2文本读写(写法比较简单)

5、使用stringstream序列化和反序列化(转字符串)

5.1使用ostringstream和istringstream


一、C++11线程库

        C++11的线程库在windows下能用,在Linux环境下也能用,是因为其内部的条件编译。

        对于C++线程库,必须分配了线程关联函数,线程才被创建启动。

1、每个线程都有独立的栈空间

        两个线程都可以调用Func1函数,在调用函数时,两个线程会各自创建自己的栈空间,所以回调函数的栈区变量从属于各个线程,线程间互不影响。

2、加锁的位置

        如图,Func2的执行效率更高。

        加锁时总是想着把锁放到临界区的极限边界,这是不对的。这里我们要保护的代码是++val,像Func1把锁就放在++val的前后,最短化临界区,卡在了极限的距离。但是AB线程每进行一次++操作,就要判断一次锁,会产生高频次的线程切换,影响效率。但是Func2这种加锁方式,一个线程跑完循环再放另一个线程进去,极大地减少了加锁带来的线程切换。(当然需要根据实际代码分析哈,如果这里的++val是其他执行时间较长的代码,Func2的写法就不合适了,因为会导致另一个线程饥饿)

3、CSA操作

        CAS(Compare and Swap)操作是一种常见的并发编程技术,用于保证多个线程访问同一共享资源时的数据一致性。它可以在不使用锁的情况下实现对共享变量的原子更新操作。

        CAS操作通常由三个参数组成:内存地址V、预期值A和新值B。当多个线程同时尝试更新V时,只有其中一个线程能够成功执行CAS操作,即当且仅当V的当前值等于A时,才会将其更新为B。如果V的当前值不等于A,则说明其他线程已经修改了V,那么当前线程会放弃更新操作,并重新尝试。

        CAS操作通常用于实现无锁算法,在高并发场景中可以提高程序的并发性能。但是,CAS操作也存在一些问题,例如ABA问题和自旋次数过多等,需要开发者在实际应用中注意避免和解决。

4、C++的类模板atomic(原子操作)

        atomic可以使线程并行。底层的本质就是CAS操作,写入时会去比对之前保存的值,如果不一样,说明已经有线程进行了修改,那么该线程将舍弃本次计算结果,重新计算、比对。

5、lock_guard(RAII风格的锁)/unique_lock(可随时释放锁)

保证在抛出异常、出了作用域时正确解锁互斥对象。

6、条件变量(用于互相通知)

        条件变量本身并不是线程安全的。

        一个线程在调用wait函数后,会被阻塞挂起,同时释放自己手上的锁。当另一个线程调用notify_one函数后,将重新唤醒该线程,该线程自动获得当初释放的那把锁。所以,这也是wait函数传入的形参必须是unique_lock的原因。使用条件变量控制偶数先打印的两种代码:

//写法一:
int mian()
{
	condition_variable cv;
	mutex mtx;
	int i = 0;
	bool flag = true;
	//线程1打印奇数,flag=false;
	thread t1([&cv, &mtx, &i,&flag] {
		while (i < 100)
		{
			unique_lock<mutex> ulock(mtx);//1、线程1拿到锁mtx
			while (flag == true)//2、判断成立
			{
				cv.wait(ulock);//3、线程1被wait阻塞并释放锁
			}
			cout << "t1" << this_thread::get_id << "->" << i << endl;//8、线程1执行任务
			++i;
			flag = true;
            cv.notify_one();//9、唤醒线程2
		}
		});
	//线程2打印偶数,flag=true;
	thread t2([&cv, &mtx, &i, &flag] {
		while (i <= 100)
		{
			unique_lock<mutex> ulock(mtx);//4、线程2拿到线程1刚释放的锁
			while (flag == false)//5、判断不成立
			{
				cv.wait(ulock);
			}
			cout << "t2" << this_thread::get_id << "->" << i << endl;//6、执行任务
			++i;
			flag = false;
			cv.notify_one();//7、唤醒线程1
		}
		});
	t1.join();
	t2.join();
	return 0;
}

//写法二
int main()
{
	condition_variable cv;
	mutex mtx;
	int i = 0;
	//线程1打印奇数
	thread t1([&cv, &mtx, &i] {
		while (i < 100)
		{
			unique_lock<mutex> ulock(mtx);
			cv.wait(ulock, [&i] {return i % 2; });
			cout << "t1" << this_thread::get_id << "->" << i << endl;
			++i;
			cv.notify_one();
		}
		});
	//线程2打印偶数
	thread t2([&cv, &mtx, &i] {
		while (i <= 100)
		{
			unique_lock<mutex> ulock(mtx);
			cv.wait(ulock, [&i] {return i % 2 != 1; });
			cout << "t2" << this_thread::get_id << "->" << i << endl;
			++i;
			cv.notify_one();
		}
		});
	t1.join();
	t2.join();
	return 0;
}

        如果这里不使用条件变量,而在两个线程函数中仅使用if判断奇偶,也能达到效果,因为同一时间只有一个线程能进入if判断中,直到完成++i才能放另一个线程进if判断。在此之前,另一个线程会在if判断之外疯狂轮询,加大了CPU的负担。

thread t1([&i] {
    while (i < 100000)
    {
        if (i % 2)
        {
            cout << this_thread::get_id() << "->" << i << endl;
            ++i;
            //this_thread::sleep_for(std::chrono::microseconds(500));
        }
    }
    });
    
thread t2([&i] {
    while (i <= 100000)//这里写<会有线程安全问题
    {
        if (i % 2 == 0)
        {
            cout << this_thread::get_id() << "->" << i << endl;
            ++i;
            //this_thread::sleep_for(std::chrono::microseconds(500));
        }
    }
});

二、IO流

1、C语言的输入输出缓冲区

        1、可以屏蔽掉低级I/O的实现2、可以使用这部分的内容实现“行”读取的行为。

2、C++流的概念

        C++的流有数据流动的意思。为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能。它的特性是:有序连续、具有方向性

3、流提取

        流提取是一个阻塞操作,以空格或者换行作为一段读取的结束:

        cin>>str是std::string的operator>>所支持的,它的返回值是istream。

        为什么返回值istream可以作为while循环的逻辑判断呢?这是因为ios这个父类重载了operator bool,让其支持了逻辑判断。

        从C++11开始,可以使用explicit关键字来显式声明operator bool()函数,完成istream到bool类型的转变,以避免隐式类型转换带来的问题。(这意味着其内部显式地将istream对象转换为bool类型的值,而不能进行隐式类型转换。可以避免编译器自作主张进行隐式类型转换,例如编译器将一个对象错误地转换为bool类型的值,而导致程序出现错误。)

        那么问题来了,为什么void*和bool可以被重载?其实不是他俩能被重载,而是自定义类型可以通过类内重载指定类型完成隐式类型转换。(看起来很秀,但本质上是隐式类型转换,悄悄的改变类型,上面说了,你重载了类型之后,永远猜不到它会在哪个不该转换的地方发生转换)

4、C++文件IO流

        C++根据文件内容的数据格式分为二进制文件和文本文件。采用文件流对象操作文件的一般步骤:

        1. 定义一个文件流对象

ifstream ififile(只输入用)

ofstream ofifile(只输出用)

fstream iofifile(既输入又输出用)

        2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系

        3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写

        4. 关闭文件

4.1二进制读写

// 二进制读写
struct ServerInfo
{
	char _address[32];//这里不要给string
	//string _address;
	int _port;
};

struct ConfigManager
{
public:
	ConfigManager(const char* filename)
		:_filename(filename)
	{}

	void WriteBin(const ServerInfo& info)
	{
		//ofstream ofs(_filename, ofstream::out | ofstream::binary);
		ofstream ofs(_filename, ios_base::out | ios_base::binary);
		ofs.write((char*)&info, sizeof(info));
	}

	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_filename, ios_base::in | ios_base::binary);
		ifs.read((char*)&info, sizeof(info));
	}

private:
	string _filename; // 配置文件
};

int main()
{
	ConfigManager cm("test.txt");
	ServerInfo winfo = { "192.0.0.111111111111111111", 80};
	cm.WriteBin(winfo);

	ServerInfo rinfo;
	cm.ReadBin(rinfo);

	cout << rinfo._address << endl;
	cout << rinfo._port << endl;

	return 0;
}

        二进制读写,不要对string对象进行读写操作。

4.2文本读写(写法比较简单)

class Date
{
	friend ostream& operator << (ostream& out, const Date& d);
	friend istream& operator >> (istream& in, Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
	operator bool()
	{
		// 这里是随意写的,假设输入_year为0,则结束
		if (_year == 0)
			return false;
		else
			return true;
	}
private:
	int _year;
	int _month;
	int _day;
};
istream& operator >> (istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
ostream& operator << (ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day;
	return out;
}

struct ServerInfo
{
	//char _address[32];
	string _address;
	int _port;
	Date _date;
};
class ConfigMange
{
public:
	ConfigMange(const char* filename)
		:_filename(filename)
	{}
	void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_filename);
		//写的时候必须给空格或换行
		ofs << info._address << endl;
		ofs << info._port << endl;
		//ofstream是ostream的子类,子类对象可以调用继承于父类的流插入和流提取
		ofs << info._date << endl;//只要重载自定义类型的流插入和流提取就能这么写
	}
	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address;
		ifs >> info._port;
		ifs >> info._date;
	}
private:
	string _filename;
};
int main()
{
	ConfigMange cm("filename.txt");
	ServerInfo winfo = { "111111111111111111111",8080 ,{2022,1,1} };
	cm.WriteText(winfo);

	ServerInfo rinfo;
	cm.ReadText(rinfo);
	cout << rinfo._address << endl;
	cout << rinfo._port << endl;
	cout << rinfo._date << endl;
	return 0;
}

        1、使用ofstream进行写入的时候,每一个变量写完必须给空格或换行,标定每个变量的读取结束,否则读取时会读取出错。

        2、自定义类型也可以使用流插入和流提取的写法是因为ofstream和ifstream是ostream的子类,子类对象可以调用继承于父类的流插入和流提取。(前提是自定义类型重载了流插入和流提取)

5、使用stringstream序列化和反序列化(转字符串)

5.1使用ostringstream和istringstream

不过stringstream兼具ostringstream和istringstream的功能,一般都用stringstream。

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

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

相关文章

微信小程序学习实录1(wxml文档、引入weui、双向数据绑定)

微信小程序学习实录 一、wxml文档二、新建页面快捷方式三、微信小程序引入weui四、双向数据绑定1.wxml渲染层2.js逻辑层 五、微信小程序跳转到H5 一、wxml文档 <!-- index.wxml --> <view><!-- 数据绑定 --><view><text>{{name}}</text>&…

机器学习笔记 基于OpenCV的文本检测和识别模块

一、EAST文本检测器简述 east是一种高效准确的场景文本检测器,网络模型如下。该模型是一个适用于文本检测的全卷积神经网络,输出单词或文本行的密集每像素预测。这就省去了诸如候选建议、文本区域形成和单词分割等中间步骤。后处理步骤仅包括对预测的几何形状进行阈值处理和N…

【分布式】一致性哈希和哈希槽

当我们拥有了多台存储服务器之后&#xff0c;现在有多个key&#xff0c;希望可以将这些个key均匀的缓存到这些服务器上&#xff0c;可以使用哪些方案呢&#xff1f; 1. 普通哈希取模法 1.1 直接哈希取模 这是一种最容易想到的方法&#xff0c;使用取模算法hash&#xff08;k…

前端三剑客React框架第一课入门的学习

前端三大框架React框架第一课入门的学习 前端三大框架的介绍 React:由facebook贡献&#xff0c;是一个基于javascript的前端库。它主要关注ui组件的构建&#xff0c;通过virtual dom等技术手段实现高效的渲染优化&#xff0c;可以与各种其他库和框架搭配使用&#xff0c;也有…

作为一个C++新手,我感兴趣的C++开源项目

2023年4月30日&#xff0c;周日晚上。 昨天完成了一个C项目后&#xff0c;想再开始一个C项目&#xff0c;但不知道做什么&#xff0c;于是决定看看有什么好的C开源项目。 今晚在网上逛了一圈后&#xff0c;发现了好多有趣的C开源项目。 参考文章&#xff1a; GitHub Top 10 …

【MySQL】十二,索引的概念

为什么使用索引 提高数据查询的效率。 定义 MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是帮助MySQL高效获取的数据的数据结构。 使用索引的优点 提高数据检索的效率&#xff0c;降低数据库的IO成本。通过创建唯一索引&#xff0c;可以保证数据…

科研闭环指南|学术论文撰写经验总结

前言&#xff1a;最近完成了自己人生中第一篇学术论文长文的撰写&#xff0c;从2023年4月12日完成初稿到2023年4月30日完成终稿这半个多月的时间里&#xff0c;在多位老师与师兄的帮助下&#xff0c;前前后后改了六七个版本&#xff0c;才改到大致满意的最终版&#xff08;在此…

设计模式——模板模式

导航&#xff1a; 【黑马Java笔记踩坑汇总】JavaSEJavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线设计模式牛客面试题 目录 模板模式 1、基本介绍 2、模板模式解决豆浆制作问题 3、钩子方法 4、Spring 框架AbstractApplicationContext抽象类 模板模式 …

C语言从入门到精通第16天(指针的定义与基本使用)

指针的定义与基本使用 什么是指针&#xff1f;指针变量的定义指针变量的基本使用 什么是指针&#xff1f; 在使用指针之前我们需要对指针进行初步的了解&#xff0c;首先我们要知道什么是指针&#xff1f; 通过前面的学习我们已经知道了内存的存储方式&#xff0c;他是通过一…

【LeetCode股票买卖系列:121. 买卖股票的最佳时机 | 一次遍历 | 暴力递归=>记忆化搜索=>动态规划】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

在群晖NAS上快速搭建属于自己的Git Server

群晖NAS套件中心是有Git Server套件的&#xff0c;只要在套件中心安装即可。但是需要注意的是&#xff1a; git 相关的命令需要使用 SSH 客户端连接到NAS上进操作。配置git时需要使用 管理员权限账户&#xff0c;而 push / fetch 使用的账户一般都为普通用户&#xff0c;一定要…

ChatGPT提示词工程(一):Guidelines

目录 一、说明二、安装环境三、Guidelines准则一&#xff1a;写出明确而具体的说明方法1&#xff1a;使用分隔符清楚地表示输入的不同部分方法2&#xff1a;用结构化输出&#xff1a;如直接要求它以HTML或者JSON格式输出方法3&#xff1a;请模型检查是否满足条件方法4&#xff…

01-环境搭建 尚筹网

环境搭建 一、项目结构搭建 ​ 模块关系 ​ parent模块仅仅用来确定各个Maven依赖的版本 ​ webui、component、entity模块继承自parent模块 ​ util、reverse模块属于独立工程&#xff0c;不参与继承与聚合 ​ 且webui依赖于component&#xff0c;component依赖于entity…

Cont. TF-IDF (BigData Data Mining)

Cont. 举例 例1 词频 (TF) 是一词语出现的次数除以该文件的总词语数。 假如一篇文件的总词语数是100个&#xff0c;而词语“母牛”出现了3次&#xff0c;那么“母牛”一词在该文件中的词频就是3/1000.03。 一个计算文件频率 (IDF) 的方法是文件集里包含的文件总数除以测定有多…

一些关于单链表的操作

思维导图&#xff1a; 一&#xff0c; 链表 1.1节点的结构 链表是啥样的啊&#xff1f;顾名思义链表就是一种用链子链接起来的表。那这种表是怎么样的啊&#xff1f; 这样的呗&#xff1a; 现在&#xff0c;我们知道了链表的形状了。那我们该如何用编程语言来形成这一种形状…

mysql 安装全过程(linux上二进制包安装)

介绍 mysql 是一种RDBMS 关系型数据库管理系统 Relational Database Management System 关系型数据库将数据保存在不同的表中&#xff0c;而不是放在一个大仓库内&#xff0c;增加了速度&#xff0c;提高了灵活性。 . mysql版本 5.7.x 和 8.0.x 是目前主流。2个…

RT-Thread 在线软件包改为本地软件包的方法

问题描述 RT-Thread 的软件包&#xff0c;使用时&#xff0c;需要手动通过 ENV 工具 更新到 本地的 packages 目录&#xff0c;并且 packages 目录默认不参与 Git 工程管理&#xff0c;软件包多了&#xff0c;并且偶尔需要更改软件包本身的一些代码&#xff0c;这就造成了软件项…

Spring 依赖注入源码

文章目录 依赖注入原始依赖注入方式注解方式寻找注入点注入点进行注入 从BeanFactory中找注入对象总结 依赖注入 具体代码是在AbstractAutowireCapableBeanFactory类的populateBean()方法&#xff0c;此方法中主要做的事情如下&#xff1a; 实例化之后&#xff0c;调用Instan…

【Java校招面试】基础知识(二)——Spring Framework AOP

目录 前言一、Spring Framewwork基础知识二、Spring AOP基础概念1. 切面&#xff08;Aspect&#xff09;2. 织入&#xff08;Weaving&#xff09;3. 增强&#xff08;Advice&#xff09;4. 动态代理 三、JDK动态代理1. 基本用法2. 原理分析 四、CGLib动态代理1. 基本用法2. 原理…

【五一创作】使用Resnet残差网络对图像进行分类(猫十二分类,模型定义、训练、保存、预测)(二)

使用Resnet残差网络对图像进行分类 &#xff08;猫十二分类&#xff0c;模型定义、训练、保存、预测&#xff09;(二&#xff09; 目录 &#xff08;6&#xff09;、数据集划分 &#xff08;7&#xff09;、训练集增强 &#xff08;8&#xff09;、装载数据集 &#xff08…