C ++同時実行の世界(1)-スレッド管理

0.デーモンスレッド

各プログラムには少なくとも1つのスレッドがあります。main()関数を実行するスレッドであり、残りのスレッドには独自のエントリ関数があります。スレッドは元のスレッド(エントリ関数としてmain()を持つスレッド)と同時に実行されます。main()関数が実行後に終了するのと同様に、スレッドがエントリ関数の実行を終了すると、スレッドも終了します。スレッドのstd :: threadオブジェクトを作成した後、スレッドが終了するのを待つ必要がありますが、最初にスレッドを開始する必要があります。極端な場合、スレッドの実行中に、タスクの関数オブジェクトが特定の通信メカニズムを介してパラメーターを渡すか、一連の独立した操作を実行する必要があります。シグナルを通信メカニズムに渡してスレッドを停止できます。つまり、C ++スレッドライブラリを使用してスレッドを開始することは、要約すると、std :: threadオブジェクトを構築することになります。std :: threadオブジェクトが破棄される前に決定が行われない場合、プログラムは終了します(std :: threadのデストラクタはstd :: terminate()を呼び出します)。したがって、例外が発生した場合でも、スレッドを正しく結合または切断できるようにする必要があります。スレッドを待たない場合は、スレッドが終了する前に、アクセス可能なデータが有効であることを確認する必要があります。これは新しい問題ではありません。シングルスレッドコードでは、オブジェクトが破棄されてからアクセスされ、未定義の動作も発生しますが、スレッドのライフサイクルによってこの問題が発生する可能性が高くなります。この状況は、スレッドが終了しておらず、関数が終了した場合に発生する可能性があります。この時点でも、スレッド関数は関数のローカル変数へのポインターまたは参照を保持しています。

スレッドを待つ必要がある場合、関連するstd :: treadインスタンスはjoin()を使用する必要があります。join()は、スレッドが完了するのを単純かつ無礼に待っているか、待っていません。待機中のスレッドをより柔軟に制御する必要がある場合。たとえば、スレッドが終了したかどうかを確認したり、一定期間待機したりする必要がある場合(時間を超えるとタイムアウトと判断されます)。これを行うには、条件変数や期待値(将来)など、他のメカニズムを使用してこれを実現する必要があります。つまり、join()はスレッドに対して1回しか使用できません。join()が使用されると、std :: threadオブジェクトを再度結合することはできません。joinable()を使用すると、no(false)が返されます。 。スレッドをデタッチする場合は、スレッドの開始後にdetach()を直接使用してデタッチできます。対応するスレッドを待つ予定の場合は、join()を呼び出す場所を慎重に選択する必要があります。スレッドの実行後に例外が発生し、join()呼び出しの前にスローされた場合、この呼び出しはスキップされることを意味します。スローされた例外によってアプリケーションが終了するのを防ぐには、決定を下す必要があります。一般に、例外なしでjoin()を使用する傾向がある場合は、ライフサイクルの問題を回避するために、例外処理中にjoin()を呼び出す必要があります。detach()を使用すると、スレッドがバックグラウンドで実行されます。つまり、メインスレッドはスレッドと直接対話できません。つまり、このスレッドの終了を待機しません。スレッドがデタッチされている場合、それを参照できるstd :: threadオブジェクトはありません。デタッチされたスレッドはバックグラウンドで実行されるため、デタッチされたスレッドは追加できません。ただし、C ++ランタイムライブラリは、スレッドが終了したときに関連リソースを正しく回復でき、バックグラウンドスレッドとコントロールC ++ランタイムライブラリの所有権が処理されることを保証します。

個別のスレッドは通常、デーモンスレッドと呼ばれます。UNIXでは、デーモンスレッドは、ユーザーインターフェイスがなく、バックグラウンドで実行されるスレッドを指します。この種のスレッドの特徴は、長時間実行されることです。スレッドのライフサイクルは、特定のアプリケーションから終了し始めたり、バックグラウンドでファイルシステムを監視したり、キャッシュをクリーンアップしたり、最適化したりする場合があります。データ構造。一方、スレッドを分離する反対側は、スレッドがいつ終了するかを判断することしかできず、「ファイアアンドフォーゲット」タスクはこのスレッド方法を使用します。実行スレッドのないstd :: threadオブジェクトではdetach()を使用できません。これはjoin()の条件でもあり、同じ方法でチェックする必要があります-std :: threadオブジェクトがt.joinable()を使用する場合、trueを返します。detach()を使用できます。

1.パラメータをスレッド関数に渡します

デフォルトのパラメータはスレッドに依存しないメモリにコピーする必要があります。パラメータが参照の形式であっても、新しいスレッドでアクセスできます。

#include <thread>
#include <iostream>
#include <string>

const void test(const int i, const std::string str)
{
	std::cout << str << " is " << i << std::endl;
}

void main()
{
	std::thread task(test, 4, "the number");
	task.join();
}

std :: move()②でt2を明示的に作成すると、t1の所有権がt2に移ります。その後、t1は実行スレッドに関連付けられなくなり、some_functionを実行する関数はt2に関連付けられるようになります。

void some_function();
void some_other_function();
std::thread t1(some_function); // 1
std::thread t2=std::move(t1); // 2
t1=std::thread(some_other_function); // 3
std::thread t3; // 4
t3=std::move(t2); // 5
t1=std::move(t3); // 6 赋值操作将使程序崩溃

t3は、デフォルトの構築方法を使用して作成され、実行スレッドには関連付けられていません。std :: move()を呼び出してt2に関連付けられたスレッドの所有権をt3⑤に転送し、t2は名前付きオブジェクトであるため、std :: move()を明示的に呼び出します。移動操作⑤が完了した後、t1はsome_other_functionを実行しているスレッドに関連付けられ、t2はどのスレッドにも関連付けられず、t3はsome_functionを実行しているスレッドに関連付けられます。最後の移動操作は、some_functionを実行しているスレッドの所有権をt1に譲渡します。この時点で、t1にはすでに関連付けられた行(some_other_functionを実行するスレッド)があるため、ここでstd :: terminate()を直接呼び出してプログラムを終了し、実行を続行できます。終了操作は、std :: threadのデストラクタを呼び出して、すべてのオブジェクトを破棄します(C ++で例外が処理される方法と同様)。セクション2.1.1では、スレッドオブジェクトが破棄される前に、スレッドが完了するかデタッチするのを明示的に待機する必要があります。コピー時にこれらの条件も満たす必要があります(注:stdに新しい値を割り当てることはできません::スレッドオブジェクトスレッドを「破棄」する方法)。

std :: thread :: hardware_concurrency()は、新しいバージョンのC ++標準ライブラリで非常に便利な関数です。この関数は、プログラムに同時に存在できるスレッドの数を返します。たとえば、マルチコアシステムでは、戻り値はCPUコアの数になります。戻り値は単なるリマインダーであり、システム情報を取得できない場合は、関数も0を返します。ただし、これは、開始スレッドの数に関するこの関数の助けを隠すこともできません。

スレッドIDタイプはstd :: thread :: idであり、2つの方法で取得できます。最初のものは、std :: threadオブジェクトのメンバー関数get_id()を呼び出すことによって直接取得できます。std :: threadオブジェクトが実行スレッドに関連付けられていない場合、get_id()はデフォルトの構造値std :: thread :: typeを返します。これは、「スレッドなし」を意味します。2番目のスレッドIDは、現在のスレッドでstd :: this_thread :: get_id()を呼び出すことによっても取得できます(この関数はヘッダーファイルで定義されています)。

 

おすすめ

転載: blog.csdn.net/qq_35789421/article/details/114597204