前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++线程库与IO流

C++线程库与IO流

作者头像
有礼貌的灰绅士
发布2023-06-23 14:50:35
1260
发布2023-06-23 14:50:35
举报

C++11线程库

thread类

https://legacy.cplusplus.com/reference/thread/thread/

在这里插入图片描述
在这里插入图片描述

可以传一个对象过去,后面的是该对象的参数。 可以创建一个空线程,创建之后什么都不做,后面可以对这个空线程再次进行操作。 不支持拷贝构造,但是可以移动构造。

在这里插入图片描述
在这里插入图片描述

这个接口是获取当前线程id的,因为对象调用不方便,所以就封装到了命名空间当中。

在这里插入图片描述
在这里插入图片描述

https://legacy.cplusplus.com/reference/thread/this_thread/ 第二个是将本线程的时间片让给其他线程。 第三个可以设置休眠到某个时间点。 第四个是休眠时间段是多少。

原子性操作库

代码语言:javascript
复制
#include<iostream>
#include<thread>

using namespace std;
int sum = 0;
void fun(size_t num)
{
	for (size_t i = 0; i < num; ++i)
		sum++;
}
int main()
{
	thread t1(fun, 10000000);
	thread t2(fun, 10000000);
	t1.join();
	t2.join();
	cout << sum << std::endl;
	return 0;
}
在这里插入图片描述
在这里插入图片描述

因为++和- - 操作是三条汇编指令,中途有可能因为时间片的到来而切换,导致线程安全问题,C++98的解决方式是用锁来进行问题的处理,但是锁会大幅度的影响性能,并且控制不好会造成死锁问题。 因此C++11中引入了原子操作。所谓原子操作:即不可被中断的一个或一系列操作,C++11引入 的原子操作类型,使得线程间数据的同步变得非常高效。 也是CAS操作: https://baike.baidu.com/item/CAS/7371138 底层其实是,对于两个线程的++和- - ,先拿到值进行计算,结果放在寄存器当中,然后与被++或- - 的变量进行对比,如果相同就保留当前结果,然后重新进行当前的++或 - - 操作。 也就是说每次++要不就是成功,要不就是失败,重新进行++。

代码语言:javascript
复制
#include<iostream>
#include<thread>
#include<atomic>
using namespace std;
atomic<int>sum = 0;
void fun(size_t num)
{
	for (size_t i = 0; i < num; ++i)
		sum++;
}
int main()
{
	thread t1(fun, 10000000);
	thread t2(fun, 10000000);
	t1.join();
	t2.join();
	cout << sum << std::endl;
	return 0;
}
在这里插入图片描述
在这里插入图片描述

锁的其他接口

在这里插入图片描述
在这里插入图片描述

这个是判断当前线程有没有锁。

代码语言:javascript
复制
if(try_lock())
{
	//如果获取到锁就走这里
}
else
{
	//如果锁被其他线程拿走了就走这里
}
在这里插入图片描述
在这里插入图片描述

这个是递归互斥锁,防止了死锁的问题。 底层原理类似判断想获取锁的线程是不是拿到锁的线程,如果是直接进去即可。

在这里插入图片描述
在这里插入图片描述

这个是RAII操作,出了作用域自动释放锁。

在这里插入图片描述
在这里插入图片描述

与lock_gard类似。

上锁/解锁操作:lock、try_lock、try_lock_for、try_lock_until和unlock。 修改操作:移动赋值、交换(swap:与另一个unique_lock对象互换所管理的互斥量所有权)、释放(release:返回它所管理的互斥量对象的指针,并释放所有权)。 获取属性:owns_lock(返回当前对象是否上了锁)、operator bool()(与owns_lock()的功能相同)、mutex(返回当前unique_lock所管理的互斥量的指针)。

支持两个线程交替打印,一个打印奇数,一个打印偶数

这是第一种写法:

代码语言:javascript
复制
#include<iostream>
#include<mutex>
#include<thread>
using namespace std;

int main()
{
	int i = 0;
	thread t1([&]() {
		while (i < 100)
		{
			if (i % 2)
			{
				cout << this_thread::get_id() << ":" << i << endl;
				i++;
			}
		}
		});
	thread t2([&]() {
		while (i <= 100)//这里是为了防止++操作的非原子性导致最终结果可能没有100
		{
			if (i % 2 == 0)
			{
				cout << this_thread::get_id() << ":" << i << endl;
				i++;
			}
		}
		});
	t1.join();
	t2.join();
	return 0;
}
在这里插入图片描述
在这里插入图片描述

但是当前的程序每次只能有一个线程可以进入if条件中,也就是说每次都需要CPU去判断另一个不满足条件的线程,如果数值大的话会很浪费CPU的资源。

条件变量

https://legacy.cplusplus.com/reference/condition_variable/condition_variable/

在这里插入图片描述
在这里插入图片描述

这些接口是通知和接收的接口。

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
using namespace std;

int main()
{
	condition_variable cv;
	mutex tex;
	int i = 0;
	bool flag = false;
	//奇数
	thread t1([&]() {
		while (i < 100)
		{
			unique_lock<mutex> lock(tex);//加锁
			while (flag == true)
				cv.wait(lock);//阻塞
			cout << this_thread::get_id() << ":" << i << endl;
			i++;
			flag = true;
			cv.notify_one();//唤醒wait
		}
		});
	//偶数
	thread t2([&]() {
		while (i < 100)
		{
			unique_lock<mutex> lock(tex);//加锁
			while(flag == false)
				cv.wait(lock);//阻塞
			cout << this_thread::get_id() << ":" << i << endl;
			i++;
			flag = false;
			cv.notify_one();//唤醒wait
		}
		});
	t1.join();
	t2.join();
	return 0;
}

这是针对两个线程打印奇数偶数的改良,这样CPU占用率就不会太高了。

IO流

流是什么

“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数 据( 其单位可以是bit,byte,packet )的抽象描述。 C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设 备(显示器)输出的过程。这种输入输出的过程被形象的比喻为“流”。 它的特性是:有序连续、具有方向性。 为了实现这种流动,C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功 能。

C++IO流

在这里插入图片描述
在这里插入图片描述

operator bool

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
#include<iostream>

using namespace std;

class A
{
public:
	A(int a)
		:_a(a)
		,_b(2)
	{}
	operator int()
	{
		return _a + _b;
	}
private:
	int _a;
	int _b;
};
int main()
{
	A a(1);
	int b = a;//自定义类型隐式转换成内置类型
	cout << b << endl;
	return 0;
}
在这里插入图片描述
在这里插入图片描述

文件流

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

  1. 定义一个文件流对象 ifstream ifile(只输入用) ofstream ofile(只输出用) fstream iofile(既输入又输出用)
  2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
  3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
  4. 关闭文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里也不需要自己关闭文件,是RAII的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
#include<iostream>
#include<fstream>
using namespace std;
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)
	{}
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];
	int _port;
	Date _date;
};
struct ConfigManager
{
public:
	ConfigManager(const char* filename)
		:_filename(filename)
	{}
	void WriteBin(const ServerInfo& info)
	{
		ofstream ofs(_filename, ios_base::out | ios_base::binary);
		ofs.write((const char*)&info, sizeof(info));
	}
	void ReadBin(ServerInfo& info)
	{
		ifstream ifs(_filename, ios_base::in | ios_base::binary);
		ifs.read((char*)&info, sizeof(info));
	}void WriteText(const ServerInfo& info)
	{
		ofstream ofs(_filename);
		ofs << info._address << " " << info._port << " " << info._date;
	}
	void ReadText(ServerInfo& info)
	{
		ifstream ifs(_filename);
		ifs >> info._address >> info._port >> info._date;
	}
private:
	string _filename; // 配置文件
};
int main()
{
	ServerInfo winfo = { "192.0.0.1", 80, { 2022, 4, 10 } };

	// 二进制读写
	ConfigManager cf_bin("test.bin");
	cf_bin.WriteBin(winfo);
	ServerInfo rbinfo;
	cf_bin.ReadBin(rbinfo);
	cout << rbinfo._address << " " << rbinfo._port << " "<< rbinfo._date << endl;

	// 文本读写
	ConfigManager cf_text("test.text");
	cf_text.WriteText(winfo);
	ServerInfo rtinfo;
	cf_text.ReadText(rtinfo);
	cout << rtinfo._address << " " << rtinfo._port << " " <<rtinfo._date << endl;
	return 0;
}
在这里插入图片描述
在这里插入图片描述

这里二进制读写的时候ServerInfo结构体里面不能将char _address[32]变成string类型,因为读写的时候读进去的是指针,容易造成野指针的问题,所以尽量不要用二进制读写。 文本读写那里就跟cout和cin一样,他们其实都是相同的作用,都可以将任意数据类型转成字符串类型,也可以进行重载。

ostringstream

标准库三个类: istringstream、ostringstream 和 stringstream,分别用来进行流的输入、输出和输入输出操作。

代码语言:javascript
复制
#include<iostream>
#include<sstream>
using namespace std;
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)
	{}
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;
}
int main()
{
	int i = 10;
	double dl = 1.01;
	Date d(2023, 6, 19);
	ostringstream oss;
	oss << i << " ";
	oss << dl << " ";
	oss << d << endl;
	string str = oss.str();
	cout << str << endl;
	return 0;
}
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-06-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++11线程库
    • thread类
      • 原子性操作库
        • 锁的其他接口
          • 支持两个线程交替打印,一个打印奇数,一个打印偶数
            • 条件变量
            • IO流
              • 流是什么
                • C++IO流
                  • operator bool
                    • 文件流
                      • ostringstream
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档