获取线程id

  • std::this_thread::get_id()
#include <iostream>#include <thread>using namespace std;std::thread::id main_thread_id = std::this_thread::get_id();    // 留神这里!!!void is_main_thread(){    cout << std::this_thread::get_id() << endl;                 // 留神这里 !!                                if (main_thread_id == std::this_thread::get_id())           // 留神这里 !!        cout << "This is the main thread" << endl;    else        cout << "This is not main thread" << endl;}int main(){    is_main_thread();    thread th(is_main_thread);        th.join();    return 0;}

输入:

1This is the main thread2This is not main thread

线程调用对象的参数传递

援用作为参数

援用作为参数时,产生参数拷贝而不是援用传递;
同时应用 std::ref() 与 thread::detach() 时,须要思考主线程中的部分属性资源(对象)是否被子线程应用,并且主线程是否先于子线程完结。

应用 thread::detach() 主线程与子线程拆散独立运行,应用 std::ref() 子线程真正援用所传递实参。当实参在主线程中领有部分属性,并且主线程先于子线程完结,那么主线程实参资源开释,子线程的形参援用便指向未定义的资源,到这里祝贺你,驶入了通向未定义的快车道。
C++ 自身有援用(&),为什么又引入 std::ref 呢?
  • 次要是思考函数式编程(如 std::thread, std::bind)在应用时,是对参数间接拷贝,而不是援用
  • std::thread, std::bind 一个函数模板,它的原理是依据已有的模板生成一个函数,然而因为std::thread, std::bind 不晓得生成的函数执行的时候,传递进来的参数是否还无效。所以它抉择参数传递而不是援用传递。如果援用传递,std::ref 和 std::cref 就排上用场了。
#include <iostream>#include <thread>using namespace std;void thread_func(const int &i){    cout << "thread_func begin" << endl;    cout << "i = " << i << endl;    const_cast<int &>(i) = i * 2;    cout << "i = " << i << endl;    for (int i=0; i<1000; ++i)    { }        // 操作 obj.i ...    cout << "thread_func end" << endl;}int main(){    cout << "main begin" << endl;    int i1 = 10;    cout << i1 << endl;    thread th1(thread_func, i1);                // 留神这里 i1 !!!    th1.join();      // th1.detach();                            // 实参 i1 被拷贝,子线程可失常运行    cout << i1 << endl;    cout << "=================" << endl;    int i2 = 10;    cout << i2 << endl;    thread th2(thread_func, std::ref(i2));      // 留神这里 std::ref(i2) !!!    th2.join();    // th2.detach();                            // 实参被援用,主线程完结时部分 i2 开释, 子线程援用开释资源,行为未定义!    cout << i2 << endl;    cout << "main end" << endl;    return 0;}

输入:

main begin10thread_func begini = 10i = 20thread_func end10                          // 留神这里输入 !!!=================10thread_func begini = 10i = 20thread_func end20                          // 留神这里输入 !!!main end

补充证实:留神类内拷贝构造函数打印

#include <iostream>#include <thread>using namespace std;class Base {public:   Base(int i = 0) : m_i(i)   {        cout << "Base(int i) " << m_i << endl;   }   Base(const Base &obj) : m_i(obj.m_i)   {       cout << "Base(const Base &obj) " << m_i << endl;   }   ~Base()   {       cout << "~Base() " << m_i << endl;   }public:   int m_i;};void thread_func(const Base &obj){    cout << "thread_func begin" << endl;    cout << obj.m_i << endl;        for (int i=0; i<1000; ++i)    { }        // 操作 obj.m_i ...        cout << "thread_func end" << endl;}int main(){    cout << "main begin" << endl;    Base obj1{1};    Base obj2{2};    cout << "=================" << endl;    thread th1(thread_func, obj1);              // 留神这里 obj1 !!!    th1.join();    // th1.detach();                            // 实参obj1被拷贝,子线程可失常运行        cout << "=================" << endl;    thread th2(thread_func, std::ref(obj2));    // 留神这里 std::ref(obj2) !!!    th2.join();    // th2.detach();                            // 实参被援用,主线程完结时部分 obj2 开释, 子线程援用开释资源,行为未定义!        cout << "=================" << endl;    cout << "main end" << endl;    return 0;}

输入:

main beginBase(int i) 1Base(int i) 2=================           // 留神这里, 拷贝构造函数被调用!!!Base(const Base &obj) 1     // (调用两次起因可见上一章)     Base(const Base &obj) 1~Base() 1thread_func begin1thread_func end~Base() 1=================           // 留神这里, 拷贝构造函数未被调用!!!thread_func begin2thread_func end=================main end~Base() 2~Base() 1

指针作为参数

指针作为形参时,形参加实参指向同一地址;
同时应用 指针 与 thread::detach() 时,须要思考主线程中的部分属性资源(对象)是否被子线程应用,并且主线程是否先于子线程完结。

应用 thread::detach() 主线程与子线程拆散独立运行,指针作为形参时,形参加实参指向同一地址。当实参在主线程中领有部分属性,并且主线程先于子线程完结,那么主线程实参资源开释,子线程的形参援用便指向未定义的资源,到这里祝贺你,驶入了通向未定义的快车道。
#include <iostream>#include <thread>using namespace std;void thread_func(const char *pt){    cout << "thread_func begin " << pt[0] << endl;    cout << "pt addr : " << static_cast<const void *>(pt) << endl;    for (int i=0; i<1000; ++i)    { }        // 操作 pt ...    cout << "thread_func end" << endl;}int main(){    cout << "main begin" << endl;    const char *pt1 = "1";                  // 部分资源    const char *pt2 = new char('2');        // 主线程治理的资源    thread th1(thread_func, pt1);    th1.join();    // th1.detach();                        // 实参、形参指向同一资源,未定义快车道        thread th2(thread_func, pt2);    th2.join();    // th2.detach();                        // 实参、形参指向同一资源,未定义快车道            cout << "pt1 addr : " << static_cast<const void *>(pt1) << endl;    cout << "pt2 addr : " << static_cast<const void *>(pt2) << endl;    delete pt2;    cout << "main end" << endl;    return 0;}

输入:【实参、形参指向同一地址】

main beginthread_func begin 1pt addr : 0x405043          // 留神这里 A thread_func endthread_func begin 2pt addr : 0xf617f0          // 留神这里 Bthread_func endpt1 addr : 0x405043         // 留神这里 A'pt2 addr : 0xf617f0         // 留神这里 B'main end

产生隐式转换的对象作为参数

当应用可能产生隐式转换的对象作为参数时,应防止产生隐式类型转换,而是是在调用线程被动生成长期对象

当应用可能产生隐式转换的对象作为参数时,将在子线程执行流应用主线程中实参结构对象。当实参在主线程中领有部分属性,并且主线程先于子线程完结,那么主线程实参资源开释,此时隐式转换结构初值未定义,到这里祝贺你,驶入了通向未定义的快车道。
#include <iostream>#include <thread>using namespace std;class Base {public:   Base(int i = 0) : m_i(i)   {        cout << "Base(int i) " << std::this_thread::get_id() << endl;   }   Base(const Base &obj) : m_i(obj.m_i)   {       cout << "Base(const Base &obj) " << std::this_thread::get_id() << endl;   }   ~Base()   {       cout << "~Base() " << std::this_thread::get_id() << endl;   }public:   int m_i;};void thread_func(const Base &){ }int main(){    cout << "main begin" << endl;    cout << "main thread id " << std::this_thread::get_id() << endl;    int i = 10;    thread th1(thread_func, i);    th1.join();    // th2.detach();                        // 隐式转换产生在子线程中,此时无奈保障主线程未执行完结,即无奈保障主线程初始资源无效 !!!    cout << "=================" << endl;    thread th2(thread_func, Base(i));      // 留神这里, Base(i) 被动生成长期对象 !!!     th2.join();    cout << "main end" << endl;    return 0;}

输入:

main beginmain thread id 1Base(int i) 2               // 留神这里,子线程中应用主线程局部变量产生隐式转换 !!!~Base() 2=================Base(int i) 1Base(const Base &obj) 1     // 留神这里,隐式转换产生在主线程(调用线程)中,初始值此时无效 !!!Base(const Base &obj) 1~Base() 1~Base() 1~Base() 3main end

总结

  • 如果根底数据类型作为参数,举荐应用值传递而不是援用。
  • 如果自定义数据类型作为参数,应防止产生隐式类型转换。做法是在调用线程被动生成长期对象,而后在子线程应用援用接管。
  • 尽量应用 join 而不是 detach(越简略越牢靠)。