C++11 thread使用可调用对象进行构造 普通函数 成员函数 lambda 仿函数,使用注意点,

结论

给thread传递【基本类型数据】int... 建议值传递

给thread传递【临时类对象】,要避免隐式转换,应该在创建thread那一行传入临时对象(TestClazz(100)),线程函数用 const引用接收,避免主线程退出对内存的非法使用

给thread传递【引用的类对象】,必须使用std::ref,线程函数用const引用或非const引用接收,

detach       

thread调用detach后,子线程与主线程分离,子线程传入参数要避免主线程提前退出导致对内存的非法使用;以及注意传入的临时对象的构造时机(如果传入临时对象); 使用ref时要注意

std::ref        不使用ref传参时,不论线程函数是const引用接收还是非引用接收,编译器默认拷贝接收(一定给你创建一个新的对象)

测试和结果

 TestClazz.h

#include<iostream>
#include<thread>
#include<mutex>

#define COUT(_MESSAGE_) std::cout << _MESSAGE_ << std::endl
#define THIS_TD_COUT(_MESSAGE_) std::cout << "线程id: " << std::this_thread::get_id() << " " << _MESSAGE_ << std::endl 
#define OBJECT_COUT(_MESSAGE_) std::cout << "对象地址: " << this << " ",THIS_TD_COUT(_MESSAGE_)

class TestClazz
{
public:
    TestClazz() { OBJECT_COUT("调用TestClazz构造"); }
    TestClazz(int _val) :val(_val) { OBJECT_COUT("调用TestClazz构造"); }
    ~TestClazz() { OBJECT_COUT("调用TestClazz析构"); }

    TestClazz(const TestClazz& t) { OBJECT_COUT("调用TestClazz拷贝构造"); }
    TestClazz(TestClazz&& t) { OBJECT_COUT("调用TestClazz移动构造"); }

    TestClazz& operator=(const TestClazz& t) { OBJECT_COUT("调用TestClazz赋值运算符"); }
    TestClazz& operator=(TestClazz&& t) { OBJECT_COUT("调用TestClazz移动赋值运算符"); }
public:
    void operator()(int n) { OBJECT_COUT("调用TestClazz仿函数"); }
    void fun(int a) { OBJECT_COUT("调用TestClazz成员函数"); }
    
    void setValue(int _val) { val = _val; }
    int getValue() { return val; }
private:
    int val = 0;
};

main.cpp

int main()
{
    THIS_TD_COUT("主线程id");

    test1();
    //test2();
    //test3();
    //test4();
    //test5();
    //test6();
    //test7();
    //test8();

    return 0;
}

普通函数

void fun1(const TestClazz& t)         test1

void fun1(const TestClazz& t) { THIS_TD_COUT("调用普通函数fun1"); }
void test1() {
    
    int a = 1;
    TestClazz t(a);    

    COUT("---td1---");
    std::thread td1(fun1, TestClazz(a));        td1.join();         // 传临时对象推荐写法 保证a在主线程构造临时对象  不怕detach
    COUT("---td1---");
    
    COUT("---td2---");
    std::thread td2(fun1, a);                   td2.join();         // 在子线程完成 线程函数参数 临时对象的创建,使用detach时可能导致未定义事件  //隐式类型转换  int -> TestClazz //
    COUT("---td2---");

    COUT("---td3---");
    std::thread td3(fun1, t);                   td3.join();           // 主线程完成 线程函数参数 临时对象的创建 不怕detach
    COUT("---td3---");

    COUT("---td4---");  
    std::thread td4(fun1, std::ref(t));          td4.join();              // 小心detach
    COUT("---td4---");  
}

td1、td2、td3都是传临时对象

td1、td3在主线程完成所需参数对象的构造,即使主线程提前结束也不会导致未定义行为

td2在子线程完成所需参数对象的构造,使用detach时主线程提前结束可能导致使用释放的内存进行参数对象的构造(反正大概那么个意思,就是未定义行为)

td4使用std::ref进行引用传参,使用detach主线程提前结束可能导致未定义行为

detach测试

总之用detach要考虑内存提前释放导致的非法访问的未定义事件,要么直接不用也行

void fun2(TestClazz& t)        test2

void fun2(TestClazz& t) { THIS_TD_COUT("调用普通函数fun2"); }
void test2() {
    int a = 1;
    TestClazz t(a);

    COUT("---td4---");
    std::thread td4(fun2, std::ref(t));          td4.join();            // 小心detach
    COUT("---td4---");
}

void fun3(TestClazz t)        test3

void fun3(TestClazz t) { THIS_TD_COUT("调用普通函数fun3"); }
void test3() {
    
    int a = 1;
    TestClazz t(a);

    COUT("---td1---");
    std::thread td1(fun3, TestClazz(a));        td1.join();
    COUT("---td1---");

    COUT("---td2---");
    std::thread td2(fun3, a);                   td2.join();
    COUT("---td2---");


    COUT("---td3---");
    std::thread td3(fun3, t);                   td3.join();
    COUT("---td3---");

    COUT("---td4---");
    std::thread td4(fun3, std::ref(t));          td4.join();
    COUT("---td4---");
}

test3和test1比较,值传递会多一层拷贝

void fun4(std::unique_ptr<TestClazz> ptr)        test4

void fun4(std::unique_ptr<TestClazz> ptr) { THIS_TD_COUT("调用普通函数fun4"); }
void test4() {

    int a = 1;
    auto ptr = std::make_unique<TestClazz>(a);
    
    COUT("---td5---");
    std::thread td5(fun4, std::move(ptr));          td5.join();  // 使用detach时可能导致在move之前 ptr超出作用域被释放,导致未定义事件
    COUT("---td5---");
}

void fun5(std::shared_ptr<TestClazz> ptr)        test5

void fun5(std::shared_ptr<TestClazz> ptr) { THIS_TD_COUT("调用普通函数fun5"); }
void test5() {

    int a = 1;
    auto ptr = std::make_shared<TestClazz>(a);

    COUT("---td5---");
    std::thread td5(fun5, ptr);          td5.join();            // 使用detach时可能导致在move之前 ptr超出作用域被释放,导致未定义事件
    COUT("---td5---");
}

类public成员函数          

private成员函数作为线程函数,可以用友元函数友元一下

test6

void test6() {

    TestClazz t(99);

    COUT("---td1---");
    std::thread td1(&TestClazz::fun, t, 1);                     td1.join();         // 调用拷贝 传入一个临时对象
    COUT("---td1---");

    COUT("---td2---");
    std::thread td2(&TestClazz::fun, &t, 1);                   td2.join();          // 小心detach
    COUT("---td2---");

    COUT("---td3---");
    std::thread td3(&TestClazz::fun, std::ref(t), 1);          td3.join();          // 小心detach
    COUT("---td3---");
 
}

仿函数对象

test7

void test7() {

    TestClazz t(99);

    COUT("---td1---");
    std::thread td1(t, 1);                     td1.join();              // 调用拷贝 传入一个临时对象 
    COUT("---td1---");

    COUT("---td3---");
    std::thread td3(std::ref(t), 1);          td3.join();               // 小心detach
    COUT("---td3---");
}

lambda表达式

test8

void test8() {
    COUT("---td1---");
    std::thread td1([](int a) {COUT("调用lambda表达式"); }, 1);              td1.join();
    COUT("---td1---");
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值