并发与多线程编程¶
约 397 个字 63 行代码 预计阅读时间 3 分钟
线程传值¶
- 使用
detach()
分离两个线程可能会导致主线程在子线程结束前就跑完了 - char*变量在线程中地址相同;使用string类型引用来解决;创建
m_thread
对象的时候复制一份临时变量
隐式转换和显示转换¶
- 隐式转换,对象在子线程进行构造;如果detach后main线程先结束;直接失败
-
显示转换,对象的构建过程和拷贝过程都是在主线程中完成的,这就确保了子线程在使用该参数时是安全的
-
临时变量传参
- 如果传递int这种简单类型,推荐使用值传递,不要用引用;
- 如果传递类对象,要避免使用隐式类型转换,必须在代码中显式转换(相当于创建一个临时变量),然后在函数参数里,用引用来接(),虽然该方法安全,但不易理解,不推荐使用,
- 不建议使用 detach(),用了非常容易出问题
传递类对象,智能指针¶
- 参数是一个拷贝出来的对象,也就是说,在子线程中修改该对象中的成员,是不会传回给主线程的
- 将“假”引用变为真引用;使用std::ref()
C++
class A {
public:
mutable int m_i; /* 添加mutable修饰,让m_i能被修改 */
/* 构造函数,此处实际上是把一个int转成类型A的对象,又称为类型转换构造函数 */
A(int a) : m_i(a) { cout << "[A::A(int a) exec]" << endl; }
/* 拷贝构造函数 */
A(const A &a) : m_i(a.m_i) { cout << "[A::A(const A &a) exec]" << endl; }
/* 析构函数 */
~A() { cout << "[A::~A() exec]" << endl; }
};
void my_print(const A &pbuf) {
pbuf.m_i = 66;
cout << "my_print pbuf.m_i = " << pbuf.m_i << endl;
return;
}
int main() {
A obj(10);
thread m_thread(my_print, ref(obj));
m_thread.join();
cout << "main obj.m_i = " << obj.m_i << endl;
cout << "Hello World!" << endl;
return 0;
}
用成员函数指针做线程函数¶
- thread(argv[])
- argv[0]:成员函数指针
- argv[1]:A类对象
- argv[2]:回调函数参数
C++
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int m_i;
/* 构造函数,此处实际上是把一个int转成类型A的对象,又称为类型转换构造函数 */
A(int a) : m_i(a) { cout << "[A::A(int a) exec]" << endl; }
/* 拷贝构造函数 */
A(const A &a) : m_i(a.m_i) { cout << "[A::A(const A &a) exec]" << endl; }
/* 析构函数 */
~A() { cout << "[A::~A() exec]" << endl; }
/* 成员函数做线程回调函数 */
void thread_work(int num)
{
cout << "thread_work exec" << endl;
}
};
int main()
{
A obj(10);
int var = 15;
/* 参数一:成员函数指针 */
/* 参数二:A类对象 */
/* 参数三:回调函数参数 */
/* 参数..:回调函数参数 */
thread m_thread(&A::thread_work, obj, var); /* obj对象会被拷贝一份传入子线程 */
// thread m_thread(&A::thread_work, ref(obj), var); /* obj对象直接传入子线程 */
// thread m_thread(&A::thread_work, &obj, var); /* 与std::ref()一样,obj对象也是直接传入子线程(&obj为取地址) */
m_thread.join();
cout << "Hello World!" << endl;
return 0;
}