C++线程传参详解

#include <iostream>
#include <thread>
using namespace std;

// void myprint(const int &argi, char *pch)
// void myprint(const int argi, char *pch)
void myprint(const int argi, const string &pch) // 发生隐式转换
{
    cout << argi << endl; // 分析认为:argi并不是外部i的引用,实际是值传递,则认为即使th执行了detach,那么子线程中用i值仍然是安全的
    // cout << pch << endl;  // 指针在detach子线程时,绝对是会有问题的
    cout << pch.c_str() << endl; // 指针在detach子线程时,绝对是会有问题的
}

int main()
{
    // 一: 创建临时对象作为线程参数
    //(1.1)要避免的陷阱(解释1)
    int i = 1;
    int &ri = i;
    char ch[] = "hello world";
    // 因为是引用 i和&ri的地址是一样的
    thread th(myprint, ri, ch); // 形参是指针 数组名也是指针,指向同一地址
    // th.join();
    th.detach(); // 使用detach的时候,不推荐用引用,绝对不能用指针
    cout << "end main" << endl;
    return 0;
}

上面代码运行时正常的,看似没有问题,

但是传参的时候,ch到底是什么时候转成string的,

如果main函数执行完,你再转成string,那也就是有问题的,因为这时候ch已经被系统回收了,再转的结果就是不可预料

事实上存在,ch都被回收了(main函数执行完了),系统才用ch去转string的可能性

经过查资料,最终应该改成

thread th(myprint, ri, string(ch));//临时构造一个string对象作为实参传递,不用发生隐式转换

那么如何确定string(ch)确实是在main函数结束前执行的呢?

下面通过一个自定义类来验证

#include <iostream>
#include <thread>
using namespace std;

class A
{
public:
    int m_i;
    A(int a) : m_i(a) { cout << "构造函数执行" << endl; }
    A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行" << endl; }
    // A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
    ~A() { cout << "析构函数执行" << endl; }
};

void myprint(const int argi, const A &a)
{
    cout << &a << endl;
}

int main()
{
    int i = 1;
    int j = 10;
    // thread th(myprint, i, j);    // 传j,调用构造函数A(int a) 将一个数字转为A对象,执行程序的时候,构造函数没来得及执行,说明构造是在子线程中完成
    thread th(myprint, i, A(j)); // 匿名对象,构造函数是在主线程中完成,然后调用拷贝构造生成一个副本,这个副本才是按照引用传递给myprint的实参,这个也是在主线程中完成的
    // thread th(myprint, i, A(j)); //在创建线程的同事构造临时对象的方法传递参数是可行的
    th.detach();
    // th.join();
    cout << "end main" << endl;
    return 0;
}

/*
thread的参数是右值引用,上例中A没有移动构造,传参的时候找不到移动构造,就会找拷贝构造
 */

再测试下

#include <iostream>
#include <thread>
using namespace std;

class A
{
public:
    int m_i;
    A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    // A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
    ~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};

void myprint(const A &a)
{
    cout << &a << ' ' << this_thread::get_id() << endl;
}

int main()
{
    cout << "主线程id:" << this_thread::get_id() << endl;
    int j = 10;
    // thread th(myprint, A(j));
    thread th(myprint, j);
    th.join();
    // th.detach();
    cout << "end main" << endl;
    return 0;
}

两种情况

1:使用 thread th(myprint, A(j));

主线程id:1

构造函数执行,thread_id:1

拷贝构造函数执行,thread_id:1

析构函数执行,thread_id:1

0x1b601f026a8 2

析构函数执行,thread_id:2

end main

2:使用 thread th(myprint, j);

主线程id:1

构造函数执行,thread_id:2

0x68b65ffb2c 2

析构函数执行,thread_id:2

end main

再进行测试,把myprint改成非引用

#include <iostream>
#include <thread>
using namespace std;

class A
{
public:
    int m_i;
    A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << ",&a=" << &a << ",&this:" << this << endl; }
    // A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
    ~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};

void myprint(const A &a)
{
    cout << &a << ' ' << this_thread::get_id() << endl;
}

int main()
{
    cout << "主线程id:" << this_thread::get_id() << endl;
    int j = 10;
    thread th(myprint, A(j));
    // thread th(myprint, j);
    th.join();
    cout << "end main" << endl;
    return 0;
}

1:使用 void myprint(const A &a);

主线程id:1

构造函数执行,thread_id:1

拷贝构造函数执行,thread_id:1,&a=0xef1bdff5f8,&this:0x25f927803a8

析构函数执行,thread_id:1

0x25f927803a8 2

析构函数执行,thread_id:2

end main

2:使用 void myprint(const A a);

主线程id:1

构造函数执行,thread_id:1

拷贝构造函数执行,thread_id:1,&a=0x67a15ff5f8,&this:0x1e00aaa4758

析构函数执行,thread_id:1

拷贝构造函数执行,thread_id:2,&a=0x1e00aaa4758,&this:0x67a1dff88c

0x67a1dff88c 2

析构函数执行,thread_id:2

析构函数执行,thread_id:2

end main

可以看出是拷贝一份到子线程,然后如果myprint的形参是用引用,则直接使用这份拷贝的数据,如果不是引用,是A对象接收,则会将该副本再拷贝一次。

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

std::ref()函数

#include <iostream>
#include <thread>
using namespace std;

class A
{
public:
    mutable int m_i;
    A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    // A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
    ~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};

void myprint(const A &a)//这里const不能去掉,个人觉得,因主线程中是值传递,这里如果可以修改,也是修改拷贝过来的内容,没有任何意义,所以编译器,要求传值时要const,传引用就不要const了
{
    a.m_i = 1111;
    cout << &a << ' ' << this_thread::get_id() << endl;
}

int main()
{
    cout << "主线程id:" << this_thread::get_id() << endl;
    A a(10);
    thread th(myprint, a);
    th.join();
    // th.detach();
    cout << "end main" << ' ' << a.m_i << endl;
    return 0;
}

主线程id:1
构造函数执行,thread_id:1
拷贝构造函数执行,thread_id:1
0x2f1a22fe9f8 2
析构函数执行,thread_id:2
end main 10
析构函数执行,thread_id:1

上面代码是修改备份中的内容,所以主线程中的a不会被修改。

如果修改,可以使用ref传引用

#include <iostream>
#include <thread>
using namespace std;

class A
{
public:
    int m_i;
    A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    // A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
    ~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};

void myprint(A &a)
{
    a.m_i = 1111;
    cout << &a << ' ' << this_thread::get_id() << endl;
}

int main()
{
    cout << "主线程id:" << this_thread::get_id() << endl;
    A a(10);
    thread th(myprint, ref(a));
    th.join();
    // th.detach();
    cout << "end main" << ' ' << a.m_i << endl;
    return 0;
}
主线程id:1
构造函数执行,thread_id:1
0xb5d03ff994 2
end main 1111
析构函数执行,thread_id:1

传递智能指针

#include <iostream>
#include <thread>
using namespace std;

class A
{
public:
    int m_i;
    A(int a) : m_i(a) { cout << "构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    A(const A &a) : m_i(a.m_i) { cout << "拷贝构造函数执行,thread_id:" << this_thread::get_id() << endl; }
    // A(A &&a) : m_i(a.m_i) { cout << "移动函数执行" << endl; }
    ~A() { cout << "析构函数执行,thread_id:" << this_thread::get_id() << endl; }
};

void myprint(A &a)
{
    a.m_i = 1111;
    cout << &a << ' ' << this_thread::get_id() << endl;
}

void myprint2(unique_ptr<int> uptr)
{
    cout << this_thread::get_id() << endl;
}

int main()
{
    cout << "主线程id:" << this_thread::get_id() << endl;
    // A a(10);
    // thread th(myprint, ref(a));

    thread th(myprint2, unique_ptr<int>(new int(100)));
    // unique_ptr<int> muptr(new int(100));
    // thread th(myprint2, move(muptr));//unique_ptr已经删除了拷贝构造,所以这里得用move

    th.join();
    // th.detach();
    cout << "end main" << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值