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

这篇具有很好参考价值的文章主要介绍了【C++11】多线程+IO流。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 

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


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 目录

一、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++11】多线程+IO流

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

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

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

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

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

2、加锁的位置

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

        如图,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(原子操作)

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

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

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

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

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

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

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

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

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

        一个线程在调用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;
}

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

//写法二
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标准类库,这些每个类都称为流/流类,用以完成某方面的功能。它的特性是:有序连续、具有方向性

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

3、流提取

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

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

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

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

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

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

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

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

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

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

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

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

【C++11】多线程+IO流文章来源地址https://www.toymoban.com/news/detail-430354.html

到了这里,关于【C++11】多线程+IO流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux学习记录——사십사 高级IO(5)--- Epoll型服务器(2)(Reactor)

    本篇基于上篇代码继续改进,很长。关于Reactor的说明在后一篇 上面的代码在处理读事件时,用的request数组是临时的,如果有数据没读完,那么下次再来到这里,就没有这些数据了。所以得让每一个fd都有自己的缓冲区。建立一个Connection类,然后有一个map结构,让这个类和每

    2024年01月20日
    浏览(59)
  • Linux学习记录——사십사 高级IO(6)--- Epoll型服务器(3)(Reactor)

    看完前两篇再看这篇,本篇将会写Reactor EpollServer.hpp中创建一个函数HandlerRequest,用它来做Recver函数的数据处理,也就是数据分析。 改一下回调函数,不向外暴露Connection类。 Main.cc中就不需要两个函数,一个计算函数就可以 处理数据那里再加上最后的步骤 回到Recver函数,调用

    2024年01月20日
    浏览(46)
  • Linux学习记录——사십오 高级IO(6)--- Epoll型服务器(3)(Reactor)

    看完前两篇再看这篇,本篇将会写Reactor EpollServer.hpp中创建一个函数HandlerRequest,用它来做Recver函数的数据处理,也就是数据分析。 改一下回调函数,不向外暴露Connection类。 Main.cc中就不需要两个函数,一个计算函数就可以 处理数据那里再加上最后的步骤 回到Recver函数,调用

    2024年01月23日
    浏览(59)
  • C++服务器框架开发11——编译调试1/cmake学习

    该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见:[C++高级教程]从零开始开发服务器框架(sylar) 上一篇:C++服务器框架开发10——日志系统1~9代码 学习到第6个视频的00:59,由于不了解编译,这次先学习下cmake。下图是CMakeLists.txt中的内容。 参考自文

    2024年02月16日
    浏览(83)
  • 安可目录入围产品名单(安可目录入围产品名单 服务器)

     2022-12-15  看点  20 推广 2021信创产品名录有: 1、中孚信息(300659): 公司是专注于信息安全领域的高新技术企业,主营业务为信息安全产品研发,生产销售及信息安全服务。公司主要产品及服务包含信息安全保密产品,商用密码产品,信息安全服务。 2、中科曙光(60

    2024年02月04日
    浏览(71)
  • 服务器IO复用reactor模式

    调试: Linux下nc命令作为客户端: nc 127.0.0.1 7777

    2024年02月10日
    浏览(48)
  • TCP服务器的演变过程:IO多路复用机制select实现TCP服务器

    手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 本节,在上一章节的基础上,将并发的实现改为IO多路复用机制,使用select管理每个新接入的客户端连

    2024年02月03日
    浏览(59)
  • linux并发服务器 —— IO多路复用(八)

    半关闭只能实现数据单方向的传输;当TCP 接中A向 B 发送 FIN 请求关闭,另一端 B 回应ACK 之后 (A 端进入 FIN_WAIT_2 状态),并没有立即发送 FIN 给 A,A 方处于半连接状态 (半开关),此时 A 可以接收 B 发送的数据,但是 A 已经不能再向 B 发送数据 close不会影响到其他进程,shutdown会

    2024年02月09日
    浏览(52)
  • 阿里云产品试用系列-云服务器 ECS

    阿里云为个人开发者或者企业用户提供云产品的免费试用,本文主要描述ECS云服务器试用体验。 如上所示,在阿里云官方网站中,可使用云服务器ECS构建应用服务。 如上所示,在阿里云控制台中,可以显示成功定制免费使用阿里云ECS云服务器。 如上所示,使用阿里云提供登

    2024年02月08日
    浏览(59)
  • 【TCP服务器的演变过程】使用IO多路复用器epoll实现TCP服务器

    手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 本节,在上一章节的基础上,将IO多路复用机制select改为更高效的IO多路复用机制epoll,使用epoll管理每

    2024年01月17日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包