C++11スレッド使用概要


std::threadはヘッダファイルに宣言されるため、std::threadを使用するにはヘッダファイルを含める必要があります.
ヘッダファイルサマリーヘッダファイルはstd::threadスレッドクラスおよびstd::swap(2つのスレッドオブジェクトを交換する)補助関数を宣言します.名前空間std::this_threadもヘッダファイルに宣言されます.C++11規格で定義されているヘッダファイルの概要を次に示します.
N 3242=11-0012ドラフト第30.3節Threads(p 1133)を参照.
amespace std {
    #define __STDCPP_THREADS__ __cplusplus
    class thread;
    void swap(thread& x, thread& y);
    namespace this_thread {
        thread::id get_id();
        void yield();
        template 
        void sleep_until(const chrono::time_point& abs_time);
        template 
        void sleep_for(const chrono::duration& rel_time);
    }        

}

ヘッダファイルは主にstd::threadクラスを宣言し、std::this_threadネーミングスペースにget_が宣言されましたid,yield,sleep_untilおよびsleep_forなどの補助関数、本章ではstd::threadクラスおよび関連関数について少し詳しく説明します.std::threadクラスの概要
std::threadはスレッドオブジェクトを表し、C++11標準宣言は以下の通りです.
namespace std {
    class thread {
        public:
            //     :
            class id;
            typedef implementation-defined native_handle_type;

            //     、             :
            thread() noexcept;
            template  explicit thread(F&& f, Args&&... args);
            ~thread();
            thread(const thread&) = delete;
            thread(thread&&) noexcept;
            thread& operator=(const thread&) = delete;
            thread& operator=(thread&&) noexcept;

            //       :
            void swap(thread&) noexcept;
            bool joinable() const noexcept;
            void join();
            void detach();
            id get_id() const noexcept;
            native_handle_type native_handle();

            //         :
            static unsigned hardware_concurrency() noexcept;
    };
}

std::threadで主に3種類の関数を宣言する:(1).構造関数、コピー構造関数及び構造関数;(2). メンバー関数;(3). 静的メンバー関数.またstd::thread::idはスレッドIDを表し、C++11は以下のように宣言する.
namespace std {
    class thread::id {
        public:
            id() noexcept;
    };

    bool operator==(thread::id x, thread::id y) noexcept;
    bool operator!=(thread::id x, thread::id y) noexcept;
    bool operator(thread::id x, thread::id y) noexcept;
    bool operator>=(thread::id x, thread::id y) noexcept;

    template
    basic_ostream&
        operator<< (basic_ostream& out, thread::id id);


    // Hash   
    template  struct hash;
    template <> struct hash<:id>;
}
std::thread詳細
std::thread構築と付与
std::threadコンストラクション関数
デフォルトコンストラクション関数(1)
thread() noexcept;
初期化コンストラクション関数(2)
template
explicit thread(Fn&& fn, Args&&... args);
コピーコンストラクタ[deleted](3)
thread(const thread&) = delete;
Moveコンストラクション関数(4)
thread(thread&& x) noexcept;
デフォルトのコンストラクション関数(1)は、空のstd::thread実行オブジェクトを作成します.
コンストラクション関数(2)を初期化し、argsによって与えられるfn関数を呼び出すstd::threadオブジェクトを作成します.
コピーコンストラクション関数(無効)(3)は、std::threadオブジェクトのコピー不可コンストラクションを意味します.
Moveコンストラクション関数(4)、moveコンストラクション関数(moveの意味はC++11の新しい概念であり、詳細は付録を参照)は、呼び出しに成功した後、xはstd::thread実行オブジェクトを表すものではありません.
スレッドのステータス:
1つのスレッドの生存期間では、複数のステータス間で変換できます.異なるオペレーティングシステムは、異なるスレッドモデルを実現し、多くの異なるスレッドステータスを定義し、各ステータスに複数のサブステータスを含めることができますが、一般的には、次のいくつかのステータスが共通しています.
1)準備完了:スケジューリングに参加し、実行されるのを待つ.スケジューリングが選択されると、直ちに実行を開始する.
2)運転:CPU占有、運転中
3)スリープ:スケジュールに参加せず、特定のイベントの発生を待つ
4)中止:実行済み、スレッドリソースの回収待ち
スレッド環境:スレッドはプロセスに存在し、プロセス内のすべてのグローバルリソースは内部の各スレッドに対して表示されます.プロセス内の典型的なグローバルリソースは以下の通りである:1)コード領域:これは現在のプロセス空間内のすべての可視関数コードを意味し、各スレッドに対しても可視である2)静的ストレージ領域:グローバル変数、静的空間3)動的ストレージ領域:スタック空間スレッド内のイベント型のローカルリソース:1)ローカルスタック空間:このスレッドを格納する関数呼び出しスタック、関数内部の局所変数など2)部分レジスタ変数:スレッドの次の実行コードのポインタオフセット量
C++のthreadオブジェクトは、通常、実行スレッド(thread of execution)を表す.マルチスレッドを使うときはjoin()関数を使うことが多いのですが、detachを使うときは効果が明らかに違います.
        thread::join()      ,        block,join                    。   ,                    。main             。 thread::join()   ,OS          ,C++         。

thread::detach()関数が呼び出されると、実行されるスレッドはスレッドオブジェクトから分離され、このスレッドはメインスレッドから分離されてバックグラウンド実行に配置される.1つのスレッドオブジェクトでは表現されなくなりました.これは2つの独立したことです.C++スレッドオブジェクトは破棄され、OSが実行するスレッドは続行される.プログラムが実行するスレッドがいつ終わるかを知りたい場合は、他のメカニズムが必要です.join()関数は、実行されたスレッドに関連付けられていないため、そのthreadオブジェクト上で呼び出されません.threadオブジェクトがスレッドを指していないため制御が失われ、オブジェクトが解析されるとスレッドはバックグラウンドで実行され続けますが、メインプログラムが終了するとスレッドが実行される保証はありません.良好な制御メカニズムがない場合や、このようなバックグラウンドスレッドが重要である場合は、detachではなくjoinを使用することが望ましい.
まだ「joinable」可能なC++スレッドオブジェクトを破棄すると、エラーとみなされます.C++スレッドオブジェクトを破棄するには、約join()関数が呼び出される(終了する)か、detach()関数が呼び出される必要があります.C++スレッドオブジェクトが破棄されてもjoinされると、例外が放出されます.
mutex:
mutexは、スレッドの同期を保証し、異なるスレッドが同じ共有データを同時に操作することを防止するために使用されます.
サンプルコード:
int cnt= 20;
mutex m;
void t1()
{
    while (cnt > 0)
    {    
        m.lock();
        
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }

        m.unlock();
    }
}
void t2()
{
    while (cnt > 0)
    {
        m.lock();
        
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }

        m.unlock();
    }
}
int main()
{
    
    thread th1(t1);
    thread th2(t2);
    
    th1.join();
    th2.join();

    return 0;
}

実行結果、cntは順次減少し、マルチスレッドによって順序が狂わない.
lock_guard:
lock_の使用guardは比較的安全で、役割ドメインに基づいており、自己ロックを解除することができ、オブジェクトが作成されるとm.lock()のように反発ロックが得られ、ライフサイクルが終了すると、スレッドが異常に終了したために他のスレッドに影響を与えないように自動的にプロファイルされます.
int cnt = 20;
mutex m;
void t1()
{
    while (cnt > 0)
    {    
        lock_guard lockGuard(m);
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }
        
    }
}
void t2()
{
    while (cnt > 0)
    {
        lock_guard lockGuard(m);
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }
    
    }
}

get_id:
スレッドIDを取得し、std::thread::idのタイプのオブジェクトを返します.
例は次のとおりです.
#include 
#include 
#include 

void foo()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
    std::thread t1(foo);
    std::thread::id t1_id = t1.get_id();

    std::thread t2(foo);
    std::thread::id t2_id = t2.get_id();

    std::cout << "t1‘s id: " << t1_id << ‘
‘; std::cout << "t2‘s id: " << t2_id << ‘
‘; t1.join(); t2.join(); }

sleep_until: 
スレッドは、指定された時刻(time point)にスリープされ、スレッドが再起動されます.
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point& sleep_time );

sleep_for: 
スレッドが指定されたタイムスライス(time span)をスリープすると、スレッドが再起動されますが、スレッドスケジューリングなどの理由で、実際のスリープ時間はsleep_よりも長くなる可能性があります.durationが表すタイムスライスはもっと長い.
template< class Rep, class Period >
void sleep_for( const std::chrono::duration& sleep_duration );

#include 
#include 
#include 

int main()
{
    std::cout << "waiter" << std::endl;
    std::chrono::milliseconds dura( 1000 );
    std::this_thread::sleep_for( dura );
    std::cout << "Waited 1000 ms
"; }