C++线程库与IO流

22 篇文章 0 订阅

C++11线程库

thread类

https://legacy.cplusplus.com/reference/thread/thread/
在这里插入图片描述
可以传一个对象过去,后面的是该对象的参数。
可以创建一个空线程,创建之后什么都不做,后面可以对这个空线程再次进行操作。
不支持拷贝构造,但是可以移动构造。在这里插入图片描述
这个接口是获取当前线程id的,因为对象调用不方便,所以就封装到了命名空间当中。
在这里插入图片描述
https://legacy.cplusplus.com/reference/thread/this_thread/
第二个是将本线程的时间片让给其他线程。
第三个可以设置休眠到某个时间点。
第四个是休眠时间段是多少。

原子性操作库

#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
底层其实是,对于两个线程的++和- - ,先拿到值进行计算,结果放在寄存器当中,然后与被++或- - 的变量进行对比,如果相同就保留当前结果,然后重新进行当前的++或 - - 操作。
也就是说每次++要不就是成功,要不就是失败,重新进行++。

#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;
}

在这里插入图片描述

锁的其他接口

在这里插入图片描述
这个是判断当前线程有没有锁。

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所管理的互斥量的指针)。

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

这是第一种写法:

#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/
在这里插入图片描述
这些接口是通知和接收的接口。
在这里插入图片描述

#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

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

#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的。
在这里插入图片描述
在这里插入图片描述

#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,分别用来进行流的输入、输出和输入输出操作。

#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;
}

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ℳℓ白ℳℓ夜ℳℓ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值