【C++同時およびマルチスレッド】3_スレッド伝達パラメータ詳細、detach注意事項


スレッドidの取得
  • std::this_thread::get_id()
  • #include 
    #include 
    
    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;
    }

    出力:
    1
    This is the main thread
    2
    This 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 
    #include 
    
    using namespace std;
    
    void thread_func(const int &i)
    {
        cout << "thread_func begin" << endl;
    
        cout << "i = " << i << endl;
        const_cast(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 begin
    10
    thread_func begin
    i = 10
    i = 20
    thread_func end
    10                          //        !!!
    =================
    10
    thread_func begin
    i = 10
    i = 20
    thread_func end
    20                          //        !!!
    main end

    補足証明:注意クラス内コピー構造関数印刷
    #include 
    #include 
    
    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 begin
    Base(int i) 1
    Base(int i) 2
    =================           //     ,          !!!
    Base(const Base &obj) 1     // (           )     
    Base(const Base &obj) 1
    ~Base() 1
    thread_func begin
    1
    thread_func end
    ~Base() 1
    =================           //     ,           !!!
    thread_func begin
    2
    thread_func end
    =================
    main end
    ~Base() 2
    ~Base() 1

    パラメータとしてポインタ
    ポインタがパラメータである場合、パラメータは実パラメータと同じアドレスを指す. thread::detach()を同時に使用する場合、プライマリ・スレッドのローカル属性リソース(オブジェクト)がスレッドによって使用され、プライマリ・スレッドがサブスレッドより先に終了するかどうかを考慮する必要があります.
       thread::detach()              ,       ,           。
                  ,            ,           ,                 ,      ,            。
    
    #include 
    #include 
    
    using namespace std;
    
    void thread_func(const char *pt)
    {
        cout << "thread_func begin " << pt[0] << endl;
    
        cout << "pt addr : " << static_cast(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(pt1) << endl;
        cout << "pt2 addr : " << static_cast(pt2) << endl;
    
        delete pt2;
    
        cout << "main end" << endl;
    
        return 0;
    }

    出力:【実パラメータ、形パラメータが同じアドレスを指す】
    main begin
    thread_func begin 1
    pt addr : 0x405043          //      A 
    thread_func end
    thread_func begin 2
    pt addr : 0xf617f0          //      B
    thread_func end
    pt1 addr : 0x405043         //      A'
    pt2 addr : 0xf617f0         //      B'
    main end

    パラメータとして暗黙的に変換されたオブジェクト
    パラメータとして暗黙的変換が発生する可能性のあるオブジェクトを使用する場合、暗黙的タイプ変換は避けるべきであり、呼び出しスレッドが thread::detach()をアクティブに生成して同時に使用する場合、メインスレッド内のローカル属性リソース(オブジェクト)がスレッドによって使用され、メインスレッドがサブスレッドより先に終了するかどうかを考慮する必要がある.
                       ,                    。
                  ,            ,           ,             ,      ,            。
    #include 
    #include 
    
    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 begin
    main thread id 1
    Base(int i) 2               //     ,                    !!!
    ~Base() 2
    =================
    Base(int i) 1
    Base(const Base &obj) 1     //     ,          (    ) ,        !!!
    Base(const Base &obj) 1
    ~Base() 1
    ~Base() 1
    ~Base() 3
    main end

    パラメータとしてスマートポインタを使用する
    #include 
    #include 
    #include 
    
    using namespace std;
    
    void thread_func(unique_ptr pt)
    {
        cout << "thread_func begin" << endl;
    
        cout << *pt << endl;
    
        cout << "thread_func end" << endl;
    }
    
    int main()
    {
        cout << "main begin" << endl;
    
        unique_ptr up{new int(1)};
    
        thread th(thread_func, std::move(up));
        th.join();
    
        cout << "main end" << endl;
    
        return 0;
    }

    出力:
    main begin
    thread_func begin
    1
    thread_func end
    main end

    まとめ
  • ベースデータ型がパラメータである場合、参照ではなく値伝達を推奨します.
  • カスタムデータ型をパラメータとして使用する場合は、暗黙的なタイプ変換は避けてください.呼び出しスレッドで一時オブジェクトをアクティブに生成し、サブスレッドでリファレンス受信を使用する方法です.
  • detachではなくjoinをできるだけ使用します(簡単であればあるほど信頼できます).