c++11マルチスレッド2<<br>>

13953 ワード

この章では、threadのインタフェースについて説明します.次に、threadのインタフェース定義を示します.
 
class thread{
public:
    class id;    //  
    //construction an destruction
    //** 、  
    thread()    noexcept;
    ~thread();
    template<typename Callable, typename Args...>
    explicit thread(Callable&& func, Args&&...args);
    //copy and move
    //** 、  、  
    thread(const thread& other) = delete;
    thread(thread&& other) noexcept;

    thread& operator=(const thread& other) = delete;
    thread& operator=(thread&& other) noexcept;
    //                swap(effective c++)
    void swap(thread& other) noexcept;
    //** 、join、detach
    void join();
    void detach();
    bool joinable() const noexcept;
    //** 、class id
    id get_id() const noexcept;
    native_handle_type native_handle();
    //** 、
    static unsigned hardware_concurrency() noexcept;
}

 
一、構造:
デフォルトのコンストラクション関数は一時的に無視され、1つのコンストラクション関数しか残っていません.これは可変テンプレートパラメータのコンストラクション関数であり、完全な転送を使用しています.パラメータについて3つの点について説明します.
1、Callable関数オブジェクト(callable object、標準ライブラリという)は、function、member function、function object、lambdaの4種類があり、具体的には以下のコードを使用します.
 
 
#include<thread>
#include<iostream>

class BackgroundClass{
public:
    void operator()()const{
        std::cout << "function object" << std::endl;
    }
    void ClassMem()const{ std::cout << "member function" << std::endl; }
};
void Func(){ std::cout << "function"<<std::endl; }
int main(){
    //1
    //std::thread myThread(BackgroundClass());         myThread,  thread,         
    //     std::thread myThread((BackgroundClass()))
//        std::thread myThread{BackgroundClass()};
    std::thread myThread((BackgroundClass()));
    //2
    //std::thread myThread(&BackgroundClass::ClassMem,BackgroundClass());
    //3
    //std::thread myThread([](){std::cout << "lambda" << std::endl; });
    //4
    //std::thread myThread(Func);
    myThread.join();
    return 0;
}

2、callable objectにパラメータを渡す:
可変パラメータテンプレートで使用するように、必要なパラメータをcallable objectの後に順番に書けばよい.
threadは、彼に渡されたパラメータを元のままコピーし(実パラメータはポインタでポインタをコピーし、実パラメータはオブジェクトでオブジェクトをコピーし、形パラメータが参照であっても)、threadは関数呼び出し時に、これらのコピーしたパラメータを利用して呼び出すことに注意してください.
 
int main(){
    std::string str(“jia”);
    std::thread myThread([](std::string&str){
        str += ”feng”;
    }, str);
    myThread.join();
    std::cout << str << std::endl;    //print:jia
    return 0;
}

Lambdaが参照として参照されるのはstrを変更するためですが、threadはstrを一時オブジェクトにコピーし、この一時オブジェクトでこのlambdaを呼び出します.lambdaはこの一時オブジェクトを参照しています.スレッドが終了すると、一時オブジェクトは破棄され、strを変更する目的に達しません.変更するにはthreadにあなたが伝えた「参照」を知らせる必要があります.これはstd::ref(str)を利用して行うことができます.
td::thread myThread([](std::string&str){
    str += ”feng”;
}, std::ref(str));
//print::jiafeng

同じプロセスで同じアドレス空間があるため、ポインタを使用するとこの問題は発生しません.
std::thread myThread([](string* pstr){
    (*pstr) += ”feng”;
}, &str);
//print:jiafeng

注意インテリジェントポインタunique_ptrは、コピー構造があり、移動のみであるため、移動構造でしか参照できません.
void ProcessBigObject(std::unique_ptr<big_object>);

std::unique_ptr<big_object> p(new big_object);

std::thread t(ProcessBigObject,std::move(p));

ここでまずbig_object moveはinternal storageに移動し、関数呼び出しにmoveします.
 
3、threadオブジェクト作成効果:デフォルトのコンストラクション関数を使用してタグを作成するだけで、実質的なスレッドはありません.get_id()はデフォルト結果を返し、joinable()はfalseを返します.関数オブジェクトを使用してスレッドオブジェクトを作成すると、スレッドはすぐにロードされ、ロードに失敗すると例外が放出されます.
 
二、コピー、割り当て:
threadとunique_ptr、IOと同様に、コピー構造、付与はなく、移動構造のみ、移動付与はthreadとunique_に定められている.ptrは似ていて、中の本当のスレッドはthreadオブジェクト間でしか移行できません.2つのthreadオブジェクトの内部が同じスレッドであることはできません.2つのuniqueのように.ptrは同じオブジェクトを指すことはできません.
1、所有権移転:
 
void some_func();
void som_other_func();
std::thread t1(some_func);         //t1  some_func
std::thread t2(some_other_func);    //t2  some_other_func
t1=std::thread(some_other_func);  //        

 
一時オブジェクトはt 1の移動構造関数をアクティブにし、some_を得るother_funcの前に、t 1はまず自分の構造関数を呼び出します.t 1はdetachまたはjoinを呼び出していないため(後で詳しく説明します)、構造関数はstd::terminateプログラムクラッシュを呼び出します.
 
std::thread t3;                    //t3     
t3=std::thread(some_func);        //  t3     ,t3    some_func
std::thread t4;
t4=std::move(t3);                // t3 some_func   t4

 
2、threadオブジェクトは関数呼び出しパラメータとして、オブジェクトを返す:
 
void func1(std::thread);
func1(std::move(t1));
func1(std::thread(some_func));
std::thread g(){
    //1 return std::thread(some_func);
    std::thread t(some_func);
    return t;
}

 
三、join、detach:
 
threadオブジェクトの構築が完了した後、ライフ有効期間内にjoinまたはdetachを呼び出す必要があります.そうしないと、threadオブジェクトのライフ期間が終了し、構造関数を呼び出すとstd::terminate()が呼び出され、アプリケーションoverが呼び出されます.
 
スレッドの実行結果を待つ必要がない場合は、threadオブジェクトを構築した後にdetachを直接呼び出すと、後で遅延するのは異常な危険であり、リソースの浪費である.
 
スレッドの実行結果を待つ必要がある場合はjoinを使用する必要があります.joinを使うのが面倒なのは「joinをどこに置くの?」あまりにも前であれば、メインスレッドのブロックは補助スレッドの終了を待っていて、並列の目的を果たすことができず、あまり後ろに置くとthreadオブジェクトからjoinの呼び出し中間コードが多く、多くのコードは異常が発生する可能性が高いことを意味します.joinは呼び出さなければなりません.中間コードが例外を投げ出しても、中間コードが投げ出した例外を処理しても、joinが補助スレッドを呼び出さないとアプリケーション全体がoverになります.
 
RAIIにヒントを与え、オブジェクト管理リソース(effective c++)を使用して、オブジェクト管理threadのjoinを作成します.
 
class ScopedThread{
    std::thread m_thread;
public:
    //      ,    std::move()     
    explicit ScopedThread(std::thread t) :m_thread(std::move(t)){
        if (!m_thread.joinable()){
            //          
            throw std::logic_error(“no thread have constructed!”)
        }
    }
    ~ScopedThread(){
        m_thread.join();
    }
    //
    ScopedThread(const ScopedThread&) = delete;
    ScopedThread& operator=(const ScopedThread&) = delete;
}
//  :
int main(){
    //          ,    thread::join  
    ScopedThread(std::thread(some_func));
    some_func_with_exception();
    return 0;
}

 
四、class id:
スレッドの一意の識別クラスで、さまざまな比較オペレータが定義されているため、関連コンテナのインデックスとして使用できます.すべての空のスレッドidと同様に、2つのidが等しい場合、同じスレッドを指すか、指す2つのスレッドが実際に空である場合.std::cout<五、hardware_concurrency:
呼び出しが成功すると、現在のアプリケーションでサポートできるスレッドの数が返されます.そうしないと、0が返されます.この関数は必ずしも呼び出しに成功するわけではないので,呼び出し後に判断する.