IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++11多线程编程(三)线程传参详解,detach()大坑,成员函数做线程函数 -> 正文阅读

[C++知识库]c++11多线程编程(三)线程传参详解,detach()大坑,成员函数做线程函数

一、传递临时对象作为线程参数

1.要避免的陷阱(解释1)

  • i 并不是真正的引用传递,所以是安全的
  • mybuf 是传指针,所以是不安全的,可以将char* pmybuf改为 const string &类型,为复制的,开辟的新内存(暂时感觉没有问题)。
  • 所以不推荐用引用,绝对不可以用指针
#include <thread>
#include <iostream>

using namespace std;

void my_print(const int &i, char * pmybuf)
{
	cout << i << endl;
	cout << pmybuf << endl;
	return;
}

int main()
{
	int mvar = 1;
	int& myary = mvar; //引用是原来地址的别名,所以两个地址是相同的
	char mybuf[] = "this is a test!";
	thread mytobj(my_print, mvar, mybuf); //第一个参数是函数名,后面的为函数参数
	//mytobj.join();
	//假设现在没有join,使用的是detach(),主线程和子线程分开执行
	//但是这个里面的i,不是引用,相当于把mvar复制给i了,其实是一个值
	//所以虽然主线程比子线程先结束,但程序是安全的,但不建议这样使用
	mytobj.detach();
	cout << "Ilove chain !" << endl;

	return 0;
}

2.要避免的陷阱(解释2)

  • 有种可能是:主线程都执行完了,mybuf已经被回收了,此线程才开始,才有mybuf到pbuf的转换。事实上存在mybuf都被回收了,系统采用mybuf转pbuf的现象,就会出错。
#include <thread>
#include <iostream>

using namespace std;

void my_print(const int &i, const string &pmybuf)
{
	cout << i << endl;
	cout << pmybuf.c_str() << endl;
	return;
}

int main()
{
	int mvar = 1;
	int& myary = mvar; 
	char mybuf[] = "this is a test!";
	thread mytobj(my_print, mvar, mybuf); //但是mybuf到底什么时候转换成string
	mytobj.detach();
	cout << "I love chain !" << endl;

	return 0;
}

改为:

#include <thread>
#include <iostream>

using namespace std;


void my_print(const int &i, const string &pmybuf)
{
	cout << i << endl;
	cout << pmybuf.c_str() << endl;
	return;
}

int main()
{
	int mvar = 1;
	int& myary = mvar; 
	char mybuf[] = "this is a test!";
	thread mytobj(my_print, mvar, string(mybuf));  //直接将mybuf转为string生成一个临时的虚拟对象,问题可以解决。 
	mytobj.detach();
	cout << "I love chain !" << endl;

	return 0;
}

验证

#include <thread>
#include <iostream>

using namespace std;

class A
{
public:
	int m_i;
	//类型转换构造函数,可以把一个int 转换成一个类a对象
	A(int a) :m_i(a)
	{
		cout << "[A::A(int a)构造函数执行]" << endl;
	}
	A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << endl; }
	~A() { cout << "[A::A()析构函数执行]" << endl; }
};

void my_print(const int& i, const A &pmybuf)
{
	cout << &pmybuf << endl;
	return;
}

int main()
{
	int mvar = 1;
	int mysecondpar = 12;
	//我们希望 mysecondpar 转成A类型对象传递给myprint线程函数的第二个参数
	//我们希望用mysecondpar构造一个对象,但是验证并没有构建出来,主线程执行完后
	//mysecondpar已经被回收了,导致后面对象根本构造不出来
	thread mytobj(my_print, mvar, mysecondpar);
	mytobj.detach();
	cout << "I love chain !" << endl;
	return 0;
}

尝试构造一个类A的临时对象

#include <thread>
#include <iostream>

using namespace std;

class A
{
public:
	int m_i;
	//类型转换构造函数,可以把一个int 转换成一个类a对象
	A(int a) :m_i(a)
	{
		cout << "[A::A(int a)构造函数执行]" << endl;
	}
	A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << endl; }
	~A() { cout << "[A::A()析构函数执行]" << endl; }
};

void my_print(const int& i, const A &pmybuf)
{
	cout << &pmybuf << endl;
	return;
}

int main()
{
	int mvar = 1;
	int mysecondpar = 12;
	//使用一个临时的对象转换,在创建线程的同时传递一个临时对象是可行的。
	thread mytobj(my_print, mvar, A(mysecondpar));
	mytobj.detach();
	cout << "I love chain !" << endl;
	return 0;
}

3.总结

  • 只要用临时构造函数作为参数的传递给线程,那么就一定能够执行完毕前把第二个参数构造出来,从而确保即便detach(),子线程也能安全运行。
    主要针对 detach()
  • 如传递int类型参数,都是值传递,不要用引用,防止节外生枝
  • 如果传递类对象,不要隐式类型转换。全部都在创建线程这一行就构建出临时对象,然后在函数参数里用引用来接,如果不用引用,否则系统还会多构造一个对象,造成浪费。
  • 终极结论:建议不是用detach(),只是用join()。这样就不存在局部变量失效,线程对内存的非法使用。

二、临时对象作为线程参数继续讲

1.线程id概念

  • 每个线程不管是主线程还是子线程,每个线程世纪上都对应这一个数字,并且不相同。
  • 线程id可以用c++标准库里面的函数:std::this_thread::get_id()来获取。

2.临时对象构造时机抓捕

#include <thread>
#include <iostream>

using namespace std;

class A
{
public:
	int m_i;
	A(int a) :m_i(a)
	{
		//获取线程 id
		cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
	}
	A(const A &a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
	~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
};

void my_print(const int& i, const A &pmybuf)
{
	cout << &pmybuf << endl;
	return;
}

void my_print2(const A& pmybuf)
{
	cout << "子线程 myprint 的参数地址是:" << &pmybuf <<  "---thread_id = " << std::this_thread::get_id() << endl;
}

int main()
{
	cout << "主线程 --- thread_id = " << std::this_thread::get_id() << endl;
	int mvar = 1;
	int mysecondpar = 12;
	// 不使用临时对象创建线程,则在子线程中构造对象,会导致错误
	thread mytobj(my_print2, mvar);
	// 不使用临时对象创建线程,则在子线程中构造对象,线程id和主线程不同,会导致错误
	//thread mytobj(my_print2, A(mvar));
	//使用临时对象创建线程,则在主线程中构造对象,线程id和主线程相同,没有错误
	mytobj.join();
	
	cout << "I love chain !" << endl;
	return 0;
}

如果传引用:执行一次拷贝函数
在这里插入图片描述
如果不传引用:执行两次拷贝函数
在这里插入图片描述
所以传类对象,一般要用引用传;

三、传递类对象、智能指针作为线程参数

#include <thread>
#include <iostream>

using namespace std;

class A
{
public:
	mutable int m_i; //不管在什么情况下该值都可以修改
	A(int a) :m_i(a)
	{
		//获取线程 id
		cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
	}
	A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
	~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
};

void my_print(const int& i, const A& pmybuf)
{
	cout << &pmybuf << endl;
	return;
}

void my_print2(const A& pmybuf)
{
	pmybuf.m_i = 199;
	cout << "子线程 myprint 的参数地址是:" << &pmybuf << "---thread_id = " << std::this_thread::get_id() << endl;
}

int main()
{
	A myobj(10);
	thread mytobj(my_print2, myobj);
	//虽然在 my_print2 中修改了m_i,但是并没有修改myobj的m_i值,但要是类传引用要加const
	//thread mytobj(my_print2, std::ref(myobj));
	//把真正的myobj的引用传进去,则可以修改值
	mytobj.join();
	return 0;
}

std::ref函数(智能指针作为参数传递)

#include <thread>
#include <iostream>

using namespace std;

class A
{
public:
	mutable int m_i; //不管在什么情况下该值都可以修改
	A(int a) :m_i(a)
	{
		//获取线程 id
		cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
	}
	A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
	~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
};

int main()
{
	unique_ptr<int> myp(new int(100));
	thread mytobj(my_print2, std::move(myp));//把指针转到了pzn中,myp变成空的
	mytobj.join();
	//如果用detach()则有可能出错,有可能主线程都已经执行完了,但是还没有把只能呢个指针转过去
	return 0;
}

四、用成员函数指针做线程函数

#include <thread>
#include <iostream>

using namespace std;

class A
{
public:
	int m_i; //不管在什么情况下该值都可以修改
	A(int a) :m_i(a)
	{
		//获取线程 id
		cout << "[A::A(int a)构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl;
	}
	A(const A& a) :m_i(a.m_i) { cout << "[A::A(const A)拷贝构造函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }
	~A() { cout << "[A::A()析构函数执行]" << this << "---thread_id = " << std::this_thread::get_id() << endl; }

	//线程的执行入口
	void thread_work(int num) 
	{
		cout << "[子线程 thread_work 执行]" << endl;
	}
};

int main()
{
	A myobj(10);
	//第一个为线程的入口函数,第二个为类对象,第三个为函数的参数
	std::thread mytobj(&A::thread_work, myobj, 15);
	//如果传智能指针,则用的就是myobj本身,此时用 detach() 则错了
	//std::thread mytobj(&A::thread_work, std::ref(myobj), 15);
	mytobj.join();
	return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:12:28  更:2022-02-26 11:14:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:17:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码