C++ マルチスレッドプログラミング
-
Upload
kohsuke-yuasa -
Category
Engineering
-
view
14.644 -
download
1
description
Transcript of C++ マルチスレッドプログラミング
2014/08/30
C++マルチスレッドプログラミング
@hotwatermorning
1
発表者自己紹介
✤ @hotwatermorning✤ Sapporo.cpp運営メンバー✤ C++ポケットリファレンス執筆✤ DTMer✤(ゲームプログラミングはやったことない)
2
発表用に用意したソース
✤こちらに https://github.com/hotwatermorning/Ohotech10-SampleGame✤ライブラリのパスを設定してVisual Studio 2013でビルドすると、サンプルゲームがビルドできる
3
本日のレシピ
✤マルチスレッドプログラミングの概略✤ C++のスレッドライブラリ✤実践「task_queueクラス」
4
マルチスレッドプログラミングの概略
5
マルチスレッドプログラム
✤スレッド( プログラム中の実行の流れ)を複数もつプログラム
✤マルチコアCPUの各コア上でスレッドを動かせば、同時に複数の処理を実行できる
6
void ThreadProcess1() { doSomething1();}
void ThreadProcess2() { doSomething2();}
int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess2);
th1.join(); th2.join();}
複数の実行の流れ
7
void ThreadProcess1() { doSomething1();}
void ThreadProcess2() { doSomething2();}
int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess2);
th1.join(); th2.join();}
複数の実行の流れ
8
void ThreadProcess1() { doSomething1();}
int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess1);
th1.join(); th2.join();}
複数の実行の流れ
9
同じ関数を渡してスレッドを作成すると
void ThreadProcess1() { doSomething1();}
int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess1);
th1.join(); th2.join();}
複数の実行の流れ
10
一つ関数が、2つのスレッドで別々に同時に実行される
ゲーム & マルチスレッド
✤非同期処理のため✤パフォーマンス向上のため
11
スレッドによる非同期処理
✤プログラムの中に別の実行の流れを作り、元々の流れを止めずに処理を行う✤ ファイルIO/ネットワークIO
✤ 時間のかかる処理を別のスレッドで動かして、UIの流れを止めないようにする✤ 音楽の再生
✤ ディスプレイの更新とは別のタイミングで発生するデバイスの要求に迅速に対応する
12
スレッドによるパフォーマンス向上✤マルチコアCPUの余っているコアを使って処理を行う
✤パス探索/AI思考ルーチン✤マルチスレッド化の方針
✤ タスク並列✤ 機能ごとに処理を分割し、マルチスレッドで実行する
✤ データ並列✤ 単一の機能で処理するデータを分割し、マルチスレッドで実行する
✤ http://www.isus.jp/article/game-special/designing-ai-for-games-4/
13
マルチスレッドの難点
✤スレッド間の同期処理/排他制御✤ シングルスレッドのプログラミングよりも難くなる✤ データ競合/デッドロック
✤思うようにパフォーマンスが向上しないことも✤ 処理を分割する粒度、排他制御の仕方が悪ければ逆にパフォーマンスを下げてしまう
14
既存スレッドライブラリの利用✤マルチスレッドプログラミングは、複雑になりやすく、並行処理にバグがあると発見が困難になる
✤なので充分に検証されたより高級な仕組み(ライブラリや言語拡張)が存在している場合は、それを使うほうが安心✤ TBB, PPL, Parallel STL, OpenMP, ...
✤ただし、このような仕組みを利用するには実行ファイルの他にランタイムのDLLが必要になったりすることがある
15
C++のスレッドライブラリ
16
C++のスレッド
17
✤(C++11という規格から)標準規格にスレッドが定義され、
✤スレッドを扱うクラスや、マルチスレッドプログラミングを支援するクラスが用意された
以前のC++
✤標準規格にスレッドが定義されていなかったので、C++の実装系やOSの定義をもとにマルチスレッドプログラムを書く必要があった。
✤標準規格にライブラリも用意されていなかったので、OSが用意しているスレッドライブラリやpthread, Boost.Threadなどを利用していた✤ 現在でも、C++11準拠度の低いC++実装系を使用する場合は、これらのライブラリを使用することになる
18
標準規格に定義されたクラス✤ std::thread✤ std::mutex✤ std::lock_guard/std::unique_lock✤ std::condition_variable✤ std::promise/std::future✤ std::atomicetc,...
19
std::thread
✤スレッドクラス✤スレッドを表し、管理する
✤ スレッドを作成する✤ スレッドの終了を待機する✤ スレッドを切り離す
20
スレッドの作成
✤ std::threadクラスのコンストラクタに、関数や関数オブジェクトを渡すと、
✤新たにスレッドが作成され、✤コンストラクタに渡した関数がそのスレッド上で実行される
21
// 別スレッドで呼び出したい関数void ThreadProcess() { std::cout << "Hello Thread World" << std::endl;}
void foo() { // スレッドを起動 std::thread th(ThreadProcess);
th.join();}
スレッドの作成
22
void func1() {}void func2(int x, int y) {}void func3(double &data) {}
// 関数を渡してスレッドを作成std::thread th1(func1);
// 引数も渡せるstd::thread th2(func2, 5, 10);
double value = 0.5;
// 参照を渡すにはstd::refを使用するstd::thread th3(func3, std::ref(value));
様々な方法でスレッドを作成する
23
// 関数呼び出し演算子を持つクラスのオブジェクト// (i.e., 関数オブジェクト)struct FuncObj { void operator()(std::string text) const { std::cout << text << std::endl; }} fobj;
// 関数オブジェクトを渡してスレッドを起動できるstd::thread th4(fobj, "Hello");
// ラムダ式を渡してスレッドを起動できるstd::thread th5( []{ std::cout << "World" << std::endl; } );
スレッドを作成する
24
struct ClassX { void Process() {}};
ClassX x;
// メンバ関数のアドレスとオブジェクトを渡すと、// これを結びつけて(bindして)// スレッドを起動できるstd::thread th7(&ClassX::Process, x);
スレッドを作成する
25
void DoSomething() { //時間のかかる関数 Sleep(1000/*millisec*/);}
void foo() { std::thread th(DoSomething); //スレッドを起動して
//スレッドの終了を待機する th.join();
std::cout << "joined." << std::endl;}
スレッドの終了を待機する
26
1秒後に`joined.`が出力される
void bar() { std::thread th(DoSomething);
// スレッドを切り離し th.detach(); std::cout << "detached." << std::endl;
} // thが破棄されてもスレッドは動き続ける
スレッドを切り離す
27
即座に`detached.`が出力される
std::threadクラスの注意点
28
✤なにかスレッドを作成した後は、そのスレッドを管理しているstd::threadクラスのオブジェクトがデストラクトされる前に、join() or detach()を呼び出す必要がある✤ 予期せぬバグや、パフォーマンス上の問題となりうるため✤ ムーブによって所有権を移動した場合は、移動先のオブジェクトで適切にjoin() or detach()する
std::mutexクラス
✤排他制御(Mutual Exclusion : 相互排除)を行うクラス
29
排他制御
30
✤データ競合を防ぐための仕組み✤デッドロックに注意する
int counter = 0;
void DoWork() { DoSomething();
++counter; // 複数のスレッドから同時に更新
}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
排他制御していないコード
31
int counter = 0;
void DoWork() { DoSomething();
++counter; // 複数のスレッドから同時に更新
}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
排他制御していないコード
32
合計が2000にならない
排他制御について
✤複数のスレッドから同じメモリ領域に同時にアクセスする場合の安全性
✤読み込み/読み込み → 安全✤書き込み/書き込み → 未定義動作✤読み込み/書き込み → 未定義動作
33
詳しくは http://yohhoy.hatenablog.jp/entry/2013/12/15/204116
int counter = 0;
void DoWork() { DoSomething();
++counter;
}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
排他制御していないコード
34
00A34E2E call DoSomething (0A310FFh)
00A34E33 mov eax,dword ptr ds:[00A41390h] 00A34E38 add eax,1 00A34E3B mov dword ptr ds:[00A41390h],eax (※コンパイラやコンパイルオプションによって生成されるアセンブラは異なるのでこれは一例)
データ競合
✤複数のスレッドで共有しているデータを同時に変更すると、プログラムの整合性が保てなくなる
✤これをデータ競合(Data Race)と呼ぶ
35
クリティカルセクション
✤複数のスレッドから同時にアクセスされてはならない領域
✤ここをMutexで排他制御し、整合性を保つようにする
36
int counter = 0;
void DoWork() { DoSomething(); //ここから ++counter; //ここまでは一つのスレッドしか入れないように}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
クリティカルセクション
37
int counter = 0;std::mutex mtx; // ミューテックス変数を定義void DoWork() { DoSomething(); mtx.lock(); ++counter; mtx.unlock();}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
クリティカルセクション
38
int counter = 0;std::mutex mtx;void DoWork() { DoSomething(); mtx.lock(); //ロック確保 ++counter; mtx.unlock(); //ロック解放}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
クリティカルセクション
39
アトミック性
40
✤排他制御されたクリティカルセクションは、他のスレッドからはアトミック(不可分)に実行されたように見える
✤複数の状態を変更しつつ、その途中の状態を他のスレッドから観測されたくない場合は、その範囲を排他制御によってアトミックにする
struct Statistics { void AddData( int data ); int GetAverage() const;};Statistics st;
void AddTwoData() { st.AddData(10); st.AddData(20);}
void DisplayAverage() { std::cout << st.GetAverage() << std::endl;}
排他制御していないコード
41
ここまで実行された状態でDisplayAverage()を呼び出すと
GetAverage()は10を返す
struct Statistics { void AddData( int data ); int GetAverage() const;};Statistics st;
void AddTwoData() { st.AddData(10); st.AddData(20);}
void DisplayAverage() { std::cout << st.GetAverage() << std::endl;}
排他制御していないコード
42
シングルスレッドプログラミングでは観測されない状態
Statistics st;std::mutex mtx;
void AddTwoData() { mtx.lock(); st.AddData(10); st.AddData(20); mtx.unlock();}
void DisplayAverage() { mtx.lock(); std::cout << st.GetAverage() << std::endl; mtx.unlock();}
排他制御していないコード
43
ミューテックスによってアトミック性が保証される
DisplayAverage()からはAddTwoDataの中途半端な状態が観測されないようになる
デッドロック
✤ 2つ以上のスレッドが、お互いのロックを確保しようとして処理が停止してしまう状態
✤これをデッドロックと呼ぶ✤この状態に陥ったスレッドは、回復することも、自らスレッドを終わらせることもできなくなる
44
std::mutex m1, m2;void worker1() { m1.lock(); m2.lock(); DoSomethingA(); m2.unlock(); m1.unlock();}void worker2() { m2.lock(); m1.lock(); DoSomethingB(); m1.unlock(); m2.unlock();}
デッドロック
45
タイミングが悪いことに、2つのスレッドがそれぞれ最初のロックを確保すると
std::mutex m1, m2;void worker1() { m1.lock(); m2.lock(); DoSomethingA(); m2.unlock(); m1.unlock();}void worker2() { m2.lock(); m1.lock(); DoSomethingB(); m1.unlock(); m2.unlock();}
デッドロック
46
どちらのスレッドも処理を進められなくなる
デッドロック
✤ C++標準規格のstd::mutex型では、実装系で可能な場合はresource_deadlock_would_occurをエラーコードに設定したstd::system_error例外を送出してくれるかもしれない。
✤ただしそれに頼るべきではなく、根本的にロジックの見直しをするべき
47
std::mutex m1, m2;void worker1() { m1.lock(); m2.lock(); DoSomethingA(); m2.unlock(); m1.unlock();}void worker2() { m1.lock(); m2.lock(); DoSomethingB(); m2.unlock(); m1.unlock();}
デッドロック
48
複数のロックを確保する場合は、必ず同じ順番で確保するようにする
std::mutex m1, m2;void worker1() { std::lock(m1, m2); DoSomethingA(); m2.unlock(); m1.unlock();}void worker2() { std::lock(m1, m2); DoSomethingB(); m1.unlock(); m2.unlock();}
デッドロック
49
あるいはstd::lock()関数を使用するhttp://d.hatena.ne.jp/melpon/20121006/1349503776
boost::shared_mutex
50
✤ Reader/Writerロックを実現する✤ある変数を保護するための排他制御を行いたい時、その変数へのアクセスのほとんどが読み込みで書き込みが少ない時に使用する
✤まだ標準規格には取り入れられていない
boost::shared_mutex mtx;std::string text; // 複数のスレッドで共有するデータvoid reader() { for( ; ; ) { mtx.shared_lock(); std::string tmp = text; mtx.shared_unlock(); DoSomething(tmp); }}
std::thread reader_thread1(reader);std::thread reader_thread2(reader);//...
reader側
51
shared_lockは複数のスレッドで同時に取得できる
void writer() { for( ; ; ) { std::string const tmp = GetNewText(); mtx.lock(); text = tmp; mtx.unlock(); }}std::thread writer_thread1(writer);std::thread writer_thread2(writer);//...
writer側
52
通常のロックは一つのスレッドしか取得できない
std::lock_guard/std::unique_lock
53
✤ std::mutexクラスのlock()とunlock()を常に対応せさて管理するのは面倒
✤ RAIIというイディオムを使用して、ロックの管理を楽にするためのクラス✤ RAIIについては先月の「プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌」でも発表したhttp://www.slideshare.net/hotwatermorning/cpp-resource-management
int counter = 0;std::mutex mtx;
void DoWork() { DoSomething();
mtx.lock(); ++counter; // ←ここで複雑なことをして例外が // 発生したり、 mtx.unlock(); // ←ここでunlock()を忘れると、 // ロックが確保されたままになる} // この例だと、次回の呼び出しでデッドロックする
lock()/unlock()の問題点
54
template<class Mutex>class lock_guard {public: lock_guard(Mutex &m) : m_(&m) { m_->lock(); } ̃lock_guard() { m_->unlock(); }private: Mutex *m_;};
lock_guardの実装イメージ
55
このようなクラスがあると何が可能になるか
int counter = 0;std::mutex mtx;
void DoWork() { DoSomething(); // ロック対象の型をテンプレート引数に指定して // コンストラクタに対象のオブジェクトを渡すと lock_guard<std::mutex> lock(mtx); ++counter;
}
lock_guardを使用する
56
int counter = 0;std::mutex mtx;
void DoWork() { DoSomething();
// コンストラクタ内で自動的にmtxのロック確保 lock_guard<std::mutex> lock(mtx); ++counter; // スコープを抜ける時にlock変数のデストラクタが // 呼ばれ、ロックが解放される}
lock_guardを使用する
57
int counter = 0;std::mutex mtx;
void DoWork() { DoSomething();
lock_guard<std::mutex> lock(mtx); ++counter;
}
lock_guardを使用する
58
Mutexのロックをオブジェクトの寿命に紐付けて管理できる
std::lock_guard
59
✤このロックの仕組みを実装したクラス✤テンプレート引数には、BasicLockable要件を満たす型を指定できる✤ つまり、std::mutexに限らずlock()/unlock()メンバ関数を持つ型ならなんでもいい
✤スコープ内でlock_guardクラスのオブジェクトが生きている間、ロックを確保する
std::unique_lock
60
✤ std::lock_guardクラスの機能に加えて、より高機能な仕組みをサポートする✤ 明示的にロックを解放する✤ ロックの所有権をムーブする✤ ロックの確保を試行し、成功か失敗かを取得するなど
✤スコープ内で単純なロックをかけたい場合はlock_guardを
✤ロックをより柔軟に管理したい場合はunique_lockを使用するとよい
std::condition_variable
✤条件変数と呼ばれる機能を実装したクラス✤条件変数はモニタという同期の手法を実現する✤条件を満たすまで実行をブロックし、条件を満たした時に実行を再開する
61
ユースケース
✤ Thread1は、データの準備が完了するのを待機✤ Thread2は、データを準備し、完了したらThread1に通知
62
std::condition_variable cond;std::mutex mtx;bool is_data_ready = false;
// Thread1void WaitForDataToProcess() { std::unique_lock<std::mutex> lock(mtx);
// データが準備できるまで待機する cond.wait(lock, [&]{ return is_data_ready; });
// データが準備できたので処理開始 process_data();}
condition_variableの使用例
63
// Thread2void PrepareDataForProcessing() {
retrieve_data(); prepare_data(); // データを準備する
boost::lock_guard<boost::mutex> lock(mtx);
// データが準備できたら完了フラグをセットして is_data_ready = true; // 待機状態のスレッドを起こす cond.notify_one();}
condition_variableの使用例
64
Producer/Consumerパターン
✤非同期処理のデザインパターンの一つ✤条件変数を利用して実装できる
65
66
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
キュー
データを追加する データを取り出す
67
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
キューが満杯だと
データを追加できない→ キューが空くまで待機
68
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4データが取り出されるとスペースが空く
69
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
空いたスペースにデータを追加
70
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
71
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
キューが空だと
データを取り出せない→ データが追加されるまで待機
72
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
データを追加するとキューが空でなくなる
73
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4追加されたデータを取り出す
74
Producer/Consumerパターン
Consumer1Producer1
Producer2
Producer3
Producer4
Producer5
Consumer2
Consumer3
Consumer4
template<class T>class LockedQueue { void enqueue(T val) { if(キューが満杯) { 待機; } // (1) キューの末尾にvalを追加; キューが空でなくなったら(2)に通知; } T dequeue() { if(キューが空) { 待機; } // (2) キューの先頭からデータを取り出し; 満杯だったキューが空いたら(1)に通知; }};
擬似コード
75
enqueue用/dequeue用にそれぞれ一つずつ条件変数を使う
std::promise/std::future
✤ Promiseパターンを実装したクラス✤あるタイミングで行った処理の結果を、別のタイミングで取得するための仕組み
✤マルチスレッドに限らず、非同期処理のための仕組み
76
// 値をセットするためのpromiseクラスを用意std::promise<int> p;
// promiseクラスのオブジェクトから、// 対応するfutureクラスのオブジェクトを作成std::future<int> f = p.get_future();
// pとfは"Shared State"という状態を共有しており、// これを通じてpからfへ値や例外を受け渡せる
std::promise/std::future
77
void foo() { try { int result = DoProcess(); p.set_value(result); } catch(...) { p.set_exception( std::current_exception() ); }}int bar() { // fにセットされた値を返す。 // fに値がセットされていなければ、 // セットされるまでブロックする return f.get();}
別の場所でデータを受け渡す
78
int main(){ foo(); // DoProcess(); std::cout << bar() << std::endl; //結果を取得}
スレッドを使用しない非同期処理
79
int main(){ std::thread th(foo); // 別スレッドで処理 std::cout << bar() << std::endl; //結果を取得
th.join();}
スレッドを使用して非同期処理
80
std::atomic
✤アトミック変数を実装したクラス✤アトミック変数へのアクセスは複数のスレッドから同時に行っても安全
81
std::atomic<int> counter(0);
void DoWork() { DoSomething();
++counter;
}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
ロックしなくても安全
82
std::atomic<int> counter(0);
void DoWork() { DoSomething();
++counter;
}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
ロックしなくても安全
83
合計が正しく2000になる
std::atomic<int> counter(0);
void DoWork() { DoSomething();
++counter;
}void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); }}std::thread th1(Worker);std::thread th2(Worker);
ロックしなくても安全
84
011A57B4 lock xadd dword ptr [ecx],eax
(※コンパイラやコンパイルオプションによって生成されるアセンブラは異なるのでこれは一例)
std::atomic
✤アトミック変数によって、ロックを使用せずにマルチスレッドプログラムを記述できる
✤これを利用したアルゴリズムはロックフリーアルゴリズムと呼ばれる
✤詳しくは「プログラミングの魔導書 vol.3」参照
85
std::atomic使用上の注意
✤アトミック変数は、正しく使うのが難しいので注意✤ ハードウェアアーキテクチャのメモリモデルを理解していなければ、思わぬバグの原因となる
✤ 「リリースビルドだけでなぜか落ちる」
✤なので、同期処理に不安がある時はミューテックスでちゃんとロックを掛けて処理した方が良い
86
実践「task_queueクラス」
87
実践
✤ここまでに紹介した機能を使って、マルチスレッドをサポートしたタスクキュークラスを作成する
88
int calculate(int x, int y) { /*...*/ }
// 5スレッドを内部的に立ち上げてタスクキュー作成task_queue tq(5);
// 関数をキューに追加// 結果を取得するためのfutureが返るstd::future<int> result = tq.enqueue(calculate, 10, 20); // タスクキュー内のいずれかのスレッドで// 関数が実行される
// 実行結果を取得std::cout << result.get() << std::endl;
task_queueクラス
89
90
task_queueの動作
タスクキューのスレッド1
ユーザー側のスレッド1
キュー
1.関数を追加 3. タスクを実行
タスクキューのスレッド2
タスクキューのスレッド3
タスクキューのスレッド4
ユーザー側のスレッド2
ユーザー側のスレッド3
ユーザー側のスレッド4
task_queueクラス
2.「タスク」として保持
//! タスクキューで扱うタスクを表すベースクラスstruct task_base { virtual ̃task_base() {} virtual void run() = 0;};
template<class Ret, class F, class... Args>class task_impl : public task_base {
task_impl(std::promise<Ret>&& p, F&& f, Args&&... args);
// f(args...)を呼び出してpromiseに値を設定 void run() override final;};
タスクキューの実装イメージ
91
class task_queue { // Producer/Consumerパターンを実装したキュー locked_queue<std::unique_ptr<task_base>> queue_; template<class F, class... Args> std::future<F(Args...)の戻り値の型> enqueue(F &&f, Args&&... args) { std::promise<F(Args...)の戻り値の型> p; auto f = p.get_future(); std::unique_ptr<Task> ptask( new Task(std::move(p), f, args...) ); queue_.enqueue(std::move(ptask)); return f; }
92
// 続き
void process() { for( ; ; ) { // タスクが積まれていたら、 // キューから取り出す std::unique_ptr<Task> task = locked_queue_.dequeue();
task->run(); // F(Args...)を呼び出し // promiseに値を設定する } } // 各スレッドがprocess()を実行する std::vector<std::thread> threads_;};
93
実装したソース
✤今回の発表用のサンプルソースのtaskディレクトリ
✤機能が追加されているので、前述の実装イメージよりも複雑になっているが、やっていることはほぼ同じ
94
task_queueを使用
✤デモ(サンプルゲーム)✤複数のパーティクルの移動(計算負荷を高くしてある)にtask_queueクラスを使用し、並列処理をする✤ サンプルゲームは急ごしらえのため、ソースの品質についてはご容赦ください
95
まとめ
96
✤ C++に標準で用意されたクラスを利用してマルチスレッドプログラムを作成できる
✤マルチスレッドによって、パフォーマンスを向上できる
✤マルチスレッドプログラミングでは、データ競合/デッドロックに注意する(そのための仕組みも用意されている)
参考文献
✤ 「Windowsプロフェッショナルゲームプログラミング2」秀和システム ISBN: 4798006033
✤ 「The Art of Multiprocessor Programming 並行プログラミングの原理から実践まで」 アスキー・メディアワークス ISBN: 4048679880
✤ 「プログラミングの魔導書 ~Programmers' Grimoire~ Vol.3 “Parallel, Concurrent, and Distributed Programming”」株式会社 ロングゲート ISBN:978-4-9905296-5-9
✤ 「C++ポケットリファレンス」 技術評論社 ISBN: 4774157155
✤ 「並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング」 オライリージャパン ISBN: 4873114357
97