- 追加された行はこの色です。
- 削除された行はこの色です。
#topicpath
//////////////////////////////////////////////////////////////////////////////
* 目次 [#z44d9e2e]
#contents();
//////////////////////////////////////////////////////////////////////////////
* 初めに [#s8cb5470]
- C++11 では thread がサポートされている。→ std::thread class を使う
- gcc では build 時に -pthread オプションを要求される場合がある(ビルドが通っても実行できなかった場合には、これを忘れていないか、まず疑ってみる)
//////////////////////////////////////////////////////////////////////////////
* thread の作成 [#v6807388]
- 関数 foo(), bar() をそれぞれ別の thread で実行する
// 排他出力処理
mutex print_mutex;
void print( const string &s ) {
lock_guard<mutex> lk(print_mutex);
cout << s << endl;
}
void foo() {
print( "foo" );
}
void bar() {
print( "bar" );
}
// foo(), bar() をそれぞれ別の thread で実行
thread th1( foo );
thread th2( bar );
th1.join();
th2.join();
- 関数や関数オブジェクトの呼び出し演算子が仮引数を取る場合、 std::thread のコンストラクタの第2引数以降に関数や関数オブジェクトの関数呼び出し演算子へ与える実引数を渡す。
- std::thread のコンストラクタに渡された実引数は、受け取る関数が参照渡しとして宣言されていたとしても、コピーされて std::thread クラスのオブジェクト内に保存される。
-- 上記コピーを避けたい場合は、実引数をムーブするか、 std::ref() 関数でくるんで渡す。
-- std::ref() を使用した場合、作成した thread に参照が渡されるので、元のオブジェクトの寿命に注意すること。
- thread 内で例外が発生し、 catch されないまま thread 外に送出されると、std::terminate() が呼び出され、プログラムが強制終了する。
//////////////////////////////////////////////////////////////////////////////
* thread 終了を待機する [#c8cbe7dc]
- thread の終了を待機するところで std::thread::join() を呼ぶ。
std::thread th1( foo() );
th1.join();
cout << "th1 is joined" << endl;
- join() 呼び出しが完了するまで、 join() の実行元スレッドはブロックされる。
- join() の呼び出しが完了すると、そのオブジェクト(上記例では th1)はどのスレッドも管理していない状態になる。
- 既に join() の呼び出しが完了しているオブジェクトに対して join() を呼び出すと、 std::errc::invalid_argument をエラーコードに設定した std::system_error 例外が送出される。
- std::thread::joinable() の返り値を見ることによって、そのオブジェクトの join() を問題なく呼び出せるかどうかを確認することが出来る。
- join() 呼び出し可能なオブジェクトが破棄される場合、 std::thread::detach() や std::thread::join() が自動的に呼び出されず、 std::terminate() が呼び出されてプログラムが即時に終了することになっている。
- join() 可能なオブジェクトは、破棄される前に必ず join() するか detach() を呼び出すようにしなければならない。
//////////////////////////////////////////////////////////////////////////////
* thread を手放す [#t3ea7895]
- void srd::thread::detach() を実行する
using namespace std;
void foo() {
// 時間の掛かる処理
...
}
void bar() {
thread th1( foo() );
th1.detach(); // スレッドを手放す
}
// 関数 bar() が終了しても、スレッド foo() は別スレッドにて実行中
- std::thread::detach() が呼び出されると、そのオブジェクト(上記例の th1)はどのスレッドも管理していない状態になる。
- 既に detach() が呼び出されたオブジェクトに対して detach() を呼び出すと、 std::errc::invalid_argument をエラーコードに設定された std::system_error 例外が送出される。
- std::exit() が呼ばれると、実行中のスレッドがあってもプログラムは終了する。この時、実行中のスレッドから、プログラム終了に伴い破棄されたオブジェクトにアクセスすると、未定義の動作を引き起こす。
//////////////////////////////////////////////////////////////////////////////
* thread の識別 [#x6a39824]
- std::thread::id std::thread::get_id() を呼ぶ
void foo( ostringstream &os ) {
os << "foo(): " << this_thread::get_id() << endl;
}
void bar( ostringstream &os ) {
os << "bar(): " << this_thread::get_id() << endl;
}
ostringstream os_foo;
ostringstream os_bar;
thread th1( foo(), ref( os_foo ) );
thread th2( bar(), ref( os_bar ) );
// それぞれの thread ID を得る
thread::id id1 = th1.get_id();
thread::id id2 = th2.get_id();
thread::id id3 = this_thread::get_id();
assert( id1 != is2 );
assert( id1 != is3 );
assert( id2 != is3 );
th1.join();
th2.join();
cout << os_foo.str();
cout << os_bar.str();
cout << "main thread : " << this_thread::get_id() << endl;
//////////////////////////////////////////////////////////////////////////////
* 現在の thread の処理を明け渡す [#s20f6dff]
void std::yield() noexcept;
//////////////////////////////////////////////////////////////////////////////
* 現在の thread をスリープする [#q1284bbf]
//////////////////////////////////////////////////////////////////////////////
* 並行実行できる thread の数を取得する [#te2d6f95]
//////////////////////////////////////////////////////////////////////////////
* thread を排他制御する [#d81456e1]
//////////////////////////////////////////////////////////////////////////////
* リソースのロックを管理する [#o619d861]
//////////////////////////////////////////////////////////////////////////////
* 複数のリソースをロックする [#w31f69a0]
//////////////////////////////////////////////////////////////////////////////
* ロックせずに吐いたアクセスする [#p87c1c1a]
//////////////////////////////////////////////////////////////////////////////
* スレッドセーフに1度だけ関数を呼び出す [#g8f42eac]
//////////////////////////////////////////////////////////////////////////////
* 条件変数を使用する [#kb3e1964]
//////////////////////////////////////////////////////////////////////////////
* thread をまたいで値や例外を受け渡す [#l67d5b92]
- スレッド間で非同期に値や例外を受け渡す方法として、 std::promise / std::future が提供されている
- 以下は、 std::string をやり取りする例:
#include <iostream>
#include <thread>
#include<future>
static std::mutex g_print_mutex;
void LockPrintf( const std::string & str )
{
std::lock_guard<std::mutex> lk( g_print_mutex );
std::cout << str << std::endl;
}
void ThreadFuncA( std::promise<std::string> pr )
{
std::string sendstr ="from ThreadFuncA";
pr.set_value( sendstr );
LockPrintf( "ThreadFuncA" );
}
int main( int argc, char * argv[] )
{
std::promise<std::string> prom;
std::future<std::string> futu = prom.get_future();
std::thread th_a( ThreadFuncA, move( prom ) );
std::string future_string = "Rcv from A: " + futu.get();
LockPrintf( future_string );
th_a.join();
return( 0 );
}
//============================================================================
** std::promise --- 他のスレッドに渡す値や例外を設定する [#o1b17e29]
//============================================================================
** std::future --- 他のスレッドからセットされた値や例外を取り出す [#x76936e3]
//////////////////////////////////////////////////////////////////////////////
* 非同期処理をする [#kd28f4df]
//////////////////////////////////////////////////////////////////////////////
* スレッドローカル変数を使用する [#k24f952f]