栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++并发编程(第二版)笔记

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++并发编程(第二版)笔记

2章节 线程管控
class background_task
{
public:
    void operator()() const
    {
        do_something();
        do_something_else();
    }
};
	background_task f;
	std::thread my_thread(f);
	//这里的疑惑是为什么f是调用oprator()(),我知道他重载了(),那正常不应该是代入()
	//std::thread my_thread(f())Error;
	
	std::thread my_thread((background_task ()));//Correct 
	
	std::thread my_thread{background_task()};//Correct 初始化列表
	
	//std::thread my_thread(background_task() ); Error
	//参数带入了临时变量,编译器误以为是为声明函数
	//返回值为std::thread 函数名为 my_thread  参数为background **这里注意写法**

**这里需要注意的是background_task f 传入的实参和形参是不一样的地址 **

#include
#include

class func
{
public:
	int& i;
	func(int& i_) :i(i_) {};
	void operator()() {
		for (unsigned j = 0; j < 100000; ++j) {
			do_something(i+j);
		}
	
	}
	void do_something(int i) {
		std::cout << i << std::endl;
	};

};

void oops() {
	int some_local_state = 0;
	func my_func(some_local_state);
	std::thread my_thread(my_func);
	my_thread.detach();
	//以上代码段注意一下`detach` 和join的区别
	//detach : 不等线程内代码执行继续往下运行。
	//join:		等线程内代码运行完再继续运行。  主要这里区别。
	
	//上图用到了deatch ,即表明了,等oops函数结束后。新线程 my_thread线程可能还没起来。
	//主要点是这里,毕竟新县城用到oops内的局部变量,涉及到了生命周期的问题。
}

int main() {

	oops();

	return 1;
}

在2.1.3中有讲叙到用try/catch 来进行一个异常保护的机制。但是此机制显得有点冗余。有点不太优雅。如下图

struct func;

void f{
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread t(my_func);
	try{				//此处加了一个try保护,防止在当前线程的时候出现异常。然后t线程没join执行。
		do_something_in_current_thread();	
}
catch(...){
	t.join();
	throw;
}
t.join();
}

还有另外一种较为优雅点的方法就是使用RAII机制,即类资源回收。运用析构来进行join。

class thread_gurad
{
	std::thread& t;
public:
	explicit thread_guard(std::thread& _t):t(_t){};
		~thread_guard(){
			if(t.joinable()) //判断此函数是否能join 因为一个线程只能进行一次合并(join)
				t.join;	
	}
	
	thread_guard(thread_guard const&)=delete;
	thread_guard& operator=(thread_guard const&)=delete;
}

struct func;

void f(){
	int some_local_state=0;
	func my_func(some_local_state);
	std::thread t(my_func);		//传入线程函数然后供线程进行回调。
	thread_guard g(t);			//创建一个RAII 的线程类,进行管理,当此f函数走完的时候。g类析构自动调用join			
	do_something_in_current_thread();	//执行当前所在线程需要操作的事情。
}

此方法即便do_something_in_current_thread 抛出异常,函数f()退出,以上行为仍会发生。

这里有个细节性的需注意一下,例下图:

void f(int i,std::string const& s); 	//这里的线程回调函数需要的是一个string 类型
void oops(int some_param)
{
	char buffer[1024]; // 1
	sprintf(buffer, "%i",some_param);
	std::thread t(f,3,buffer); // 2		//这里传入的是一个指针,指向一个局部变量,很可能会出现
										//当oops函数运行完成时,buff指向的局部变量会销毁,从而导致一些未定义的行为。
									
	t.detach();														
}

向线程函数传递参数的错误细节

void f(int i, std::string const& s) {

	cout << s << endl;
};

void oops(int some_param)
{
	char buffer[1024]; // 1
	sprintf_s(buffer, "%i", some_param);
	std::thread t(f, 3, string(buffer)); // 2	
	std::thread t(f, 3,buffer); // 2	ERROR 这里需要注意一个错误点就是这里传入了一个指针,然后oopes 结束的时候地址内的信息已经回收
								//			  线程回调f函数的时候已经拷贝不到buff指针内的值了,所以这个时候程序能运行但是并不能得到想要的结果。
								//我们需要先提前转换成string(buffer);

	t.detach();
}

int main() {

	oops(3);

	system("pause");

	return 1;
}}

关于std::ref ,主要讲述的是在某些场景, 代入线程函数的时候线程需盲目的拷贝下列 int&a
但是const int&a 不允许拷贝,这个时候就需要用到std::ref了,这个就是将数值转换成引用类型

引用文中一句话:

#include
#include
#include
#include

using namespace std;
void fun(const int& a)
{
	
	cout << std::hex << &a << "----" << a << endl;
	
}


int main() {

	int he = 10;
	cout << std::hex << &he << "----" << he << endl;
	thread t(fun, std::ref(he));		//!!!这里std:ref 的作用是转换成一个引用类型,而且还有一层作用类似于文本的剪切功能,
										//用于一些不允许拷贝的变量
	//thread t(fun, he); ERROR	因为fun函数期待引入一个引用类型,但是此时传入的是非常量引用。
	t.detach();
	system("pause");

	return 1;
}

如何传递参数为成员函数到线程中以及std::move 讲解也称作 移交线程归属权

#include
#include
#include
#include

using namespace std;


class X
{
public:
	void do_lengthy_work(int argc) {
		cout << argc << endl;
	};

};

int main() {

	X my_x;
	int num(10);
	std::thread t(&X::do_lengthy_work, &my_x, num);
	t.detach();
	
	std::shared_ptrm_ptr(new X);
	std::thread w(&X::do_lengthy_work, m_ptr, num);
	//如若遇上不允许拷贝只可以移动的参数时候,例unique_ptr 这个时候就需要用move移动这个变数了,比较像在文本编辑的"剪切"操
	//使用"移动"转移原对象后,就会留下一个空指针(NULL)。即为本指针unique_ptr为nullptr,
	w.detach();
	system("pause");
	return 1;
}

个人小结:
这一章中主要是对于thread 的启动,等待的介绍。

3章节 线程间共享数据 3.1 互斥量

最基础的数据保存方式:互斥量
C++中通过实例化 std::mutex 创建互斥量实例,通过成员函数lock()对互斥量上锁,unlock() 进行解锁。。C++标准库为互斥量提供了一个RAII语法 的模板类 std::lock_guard

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1037435.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号