Location via proxy:
[ UP ]
[Report a bug]
[Manage cookies]
No cookies
No scripts
No ads
No referrer
Show this form
Submit Search
Upload
C++ マルチスレッドプログラミング
•
74 likes
•
107,980 views
Kohsuke Yuasa
Follow
Ohotech 特盛 #10 ( http://ohotech.connpass.com/event/7517/ )で発表した資料です。
Read less
Read more
Report
Share
Report
Share
1 of 97
Download now
Download to read offline
More Related Content
What's hot
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
2021/4/28 に東京大学で開催された<AIセミナーシリーズ> 「Arm CPUにおけるSIMDを用いた高速計算入門」講演会で使用した資料になります。
モジュールの凝集度・結合度・インタフェース
モジュールの凝集度・結合度・インタフェース
Hajime Yanagawa
社内勉強会発表用 内容 ・モジュールの凝集度 ・モジュール結合度 ・オブジェクト指向 ・インタフェース
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
sonickun
2017/02/21 の #ssmjp で発表した際のスライドです。
非同期処理の基礎
非同期処理の基礎
信之 岩永
2014/5/10 VSハッカソン 非同期勉強会 にて発表
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
Norishige Fukushima
shared_ptrとゲームプログラミングでのメモリ管理
shared_ptrとゲームプログラミングでのメモリ管理
DADA246
メモリ管理の話
中3女子でもわかる constexpr
中3女子でもわかる constexpr
Genya Murakami
Boost.勉強会 #7 中3女子でもわかる constexpr
Constexpr 中3女子テクニック
Constexpr 中3女子テクニック
Genya Murakami
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
Fixstars Corporation
2021年8月5日開催「いまさら聞けないarmを使ったNEONの基礎と活用事例」セミナー資料です。
イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術
Kohsuke Yuasa
7/12 プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌 ユビキタス協創広場 U-cala #pronamaclrhsapporocpp で発表したスライド http://pronama.azurewebsites.net/2014/06/12/pronama-clrh-sapporocpp-at-u-cala/
一般的なチートの手法と対策について
一般的なチートの手法と対策について
優介 黒河
全ゲ連で講演したスライドになります
SAT/SMTソルバの仕組み
SAT/SMTソルバの仕組み
Masahiro Sakai
Proof Summit 2015 <http: /> で発表した、SAT/SMTソルバの仕組みです。 Proofということで、論理学的側面からの面白さを出来るだけ紹介しています。
コルーチンでC++でも楽々ゲーム作成!
コルーチンでC++でも楽々ゲーム作成!
amusementcreators
コルーチンの概要とゲーム開発への応用、C++用コルーチンライブラリHamigaki.Coroutineの使い方について説明します。
Glibc malloc internal
Glibc malloc internal
Motohiro KOSAKI
glibc mallocの解説 Video: https://youtu.be/0-vWT-t0UHg
Effective Modern C++ 勉強会 Item 22
Effective Modern C++ 勉強会 Item 22
Keisuke Fukuda
Modern Effective C++ 勉強会 Item 22の発表資料です。
いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門
Fixstars Corporation
2021年10月29日開催「いまさら聞けない!CUDA高速化入門」セミナー資料です。
明日使えないすごいビット演算
明日使えないすごいビット演算
京大 マイコンクラブ
KMCの例会講座で用いたスライドを一部編集したものです。 ビット演算を組み合わせたトリッキーな方法で様々な操作を高速に行う方法を紹介します。
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
モノビット エンジン
2017年10月27日、モノビットエンジン勉強会inサイバーコネクトツーにて、中嶋謙互が講演しました「ネットワークゲームにおける TCPとUDPの使い分け」のスライドになります。ネットワークゲームを製作する際にご参考頂けますと幸いです。 登壇者: 株式会社モノビット 取締役 CTO 中嶋謙互
プログラムを高速化する話
プログラムを高速化する話
京大 マイコンクラブ
プログラムを高速化するためのテクニックをまとめました。
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
kikairoya
What's hot
(20)
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
モジュールの凝集度・結合度・インタフェース
モジュールの凝集度・結合度・インタフェース
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
非同期処理の基礎
非同期処理の基礎
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
shared_ptrとゲームプログラミングでのメモリ管理
shared_ptrとゲームプログラミングでのメモリ管理
中3女子でもわかる constexpr
中3女子でもわかる constexpr
Constexpr 中3女子テクニック
Constexpr 中3女子テクニック
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術
一般的なチートの手法と対策について
一般的なチートの手法と対策について
SAT/SMTソルバの仕組み
SAT/SMTソルバの仕組み
コルーチンでC++でも楽々ゲーム作成!
コルーチンでC++でも楽々ゲーム作成!
Glibc malloc internal
Glibc malloc internal
Effective Modern C++ 勉強会 Item 22
Effective Modern C++ 勉強会 Item 22
いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門
明日使えないすごいビット演算
明日使えないすごいビット演算
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
プログラムを高速化する話
プログラムを高速化する話
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
Similar to C++ マルチスレッドプログラミング
Boost Tour 1.50.0 All
Boost Tour 1.50.0 All
Akira Takahashi
Boost tour 1_40_0
Boost tour 1_40_0
Akira Takahashi
Boost.勉強会 #1で使用したセッション資料。Boost 1.40.0までのほとんどのライブラリを書いてるはず。
Prosym2012
Prosym2012
MITSUNARI Shigeo
Boost9 session
Boost9 session
freedom404
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会
Koichi Sakata
boost tour 1.48.0 all
boost tour 1.48.0 all
Akira Takahashi
“Design and Implementation of Generics for the .NET Common Language Runtime”他...
“Design and Implementation of Generics for the .NET Common Language Runtime”他...
Masahiro Sakai
PLDIr#5 (2010-01-06) での紹介。
フラグを愛でる
フラグを愛でる
MITSUNARI Shigeo
Objc lambda
Objc lambda
matuura_core
Java SE 7 InvokeDynamic in JRuby
Java SE 7 InvokeDynamic in JRuby
Hiroshi Nakamura
JavaOne Tokyo 2012での発表スライド
C++0x 言語の未来を語る
C++0x 言語の未来を語る
Akira Takahashi
わんくま同盟 東京勉強会 #22 2008/07/12
SystemC Tutorial
SystemC Tutorial
kocha2012
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
akirahiguchi
Emcpp item31
Emcpp item31
mitsutaka_takeda
Effective Modern C++勉強会用資料 https://atnd.org/events/67443
[Basic 9] 並列処理 / 排他制御
[Basic 9] 並列処理 / 排他制御
Yuto Takei
早稲田大学 高度データ関連人材育成コンソーシアム D-DATa ブロックチェーン エンジニアリング 基礎編 第9回 2018/3/1
C++ Transactional Memory言語拡張の紹介
C++ Transactional Memory言語拡張の紹介
yohhoy
Boost.勉強会 #10 東京 資料
Maxwell と Java CUDAプログラミング
Maxwell と Java CUDAプログラミング
NVIDIA Japan
講演者 : エヌビディアジャパン シニアCUDAエンジニア 森野慎也 2016/1/27開催の「MaxwellとJava、C#のためのCUDA」における、講演資料です。 詳細については、イベントページをご参照ください。 http://nvidia.connpass.com/event/24764/
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
Preferred Networks
条件分岐とcmovとmaxps
条件分岐とcmovとmaxps
MITSUNARI Shigeo
“Symbolic bounds analysis of pointers, array indices, and accessed memory reg...
“Symbolic bounds analysis of pointers, array indices, and accessed memory reg...
Masahiro Sakai
PLDIr#4 (2009-12-02) での紹介。
Similar to C++ マルチスレッドプログラミング
(20)
Boost Tour 1.50.0 All
Boost Tour 1.50.0 All
Boost tour 1_40_0
Boost tour 1_40_0
Prosym2012
Prosym2012
Boost9 session
Boost9 session
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会
boost tour 1.48.0 all
boost tour 1.48.0 all
“Design and Implementation of Generics for the .NET Common Language Runtime”他...
“Design and Implementation of Generics for the .NET Common Language Runtime”他...
フラグを愛でる
フラグを愛でる
Objc lambda
Objc lambda
Java SE 7 InvokeDynamic in JRuby
Java SE 7 InvokeDynamic in JRuby
C++0x 言語の未来を語る
C++0x 言語の未来を語る
SystemC Tutorial
SystemC Tutorial
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
Emcpp item31
Emcpp item31
[Basic 9] 並列処理 / 排他制御
[Basic 9] 並列処理 / 排他制御
C++ Transactional Memory言語拡張の紹介
C++ Transactional Memory言語拡張の紹介
Maxwell と Java CUDAプログラミング
Maxwell と Java CUDAプログラミング
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
条件分岐とcmovとmaxps
条件分岐とcmovとmaxps
“Symbolic bounds analysis of pointers, array indices, and accessed memory reg...
“Symbolic bounds analysis of pointers, array indices, and accessed memory reg...
More from Kohsuke Yuasa
オーディオ用レベルメータを作ってみよう
オーディオ用レベルメータを作ってみよう
Kohsuke Yuasa
http://edge.connpass.com/event/20910/
Juceで作るオーディオアプリケーション
Juceで作るオーディオアプリケーション
Kohsuke Yuasa
SampleApp for #TokyoEDGE2015
最近のC++ @ Sapporo.cpp #5
最近のC++ @ Sapporo.cpp #5
Kohsuke Yuasa
Sapporo.cpp 札幌C++勉強会 #5で発表した、最近のC++の流れを紹介した資料。
C++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプ
Kohsuke Yuasa
Sapporo.cpp & CLR/H 合同勉強会 ( http://atnd.org/events/33614 ) で発表したスライドです。
Introduction to boost test
Introduction to boost test
Kohsuke Yuasa
Boost.勉強会#8大阪にて発表したBoost.Testの紹介
C++ template-primer
C++ template-primer
Kohsuke Yuasa
2011年 12/10 関数型都市忘年会
Read egg oven
Read egg oven
Kohsuke Yuasa
Study3 boost
Study3 boost
Kohsuke Yuasa
Sapporocpp#2 exception-primer
Sapporocpp#2 exception-primer
Kohsuke Yuasa
sapporo.cpp #2
More from Kohsuke Yuasa
(9)
オーディオ用レベルメータを作ってみよう
オーディオ用レベルメータを作ってみよう
Juceで作るオーディオアプリケーション
Juceで作るオーディオアプリケーション
最近のC++ @ Sapporo.cpp #5
最近のC++ @ Sapporo.cpp #5
C++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプ
Introduction to boost test
Introduction to boost test
C++ template-primer
C++ template-primer
Read egg oven
Read egg oven
Study3 boost
Study3 boost
Sapporocpp#2 exception-primer
Sapporocpp#2 exception-primer
C++ マルチスレッドプログラミング
1.
2014/08/30 C++ マルチスレッドプログラミング
@hotwatermorning 1
2.
発表者自己紹介 ✤ @hotwatermorning
✤ Sapporo.cpp運営メンバー ✤ C++ポケットリファレンス執筆 ✤ DTMer ✤ (ゲームプログラミングはやったことない) 2
3.
発表用に用意したソース ✤ こちらに
https://github.com/hotwatermorning/Ohotech10-SampleGame ✤ ライブラリのパスを設定してVisual Studio 2013でビルドすると、サンプルゲームがビルド できる 3
4.
本日のレシピ ✤ マルチスレッドプログラミングの概略
✤ C++のスレッドライブラリ ✤ 実践「task_queueクラス」 4
5.
マルチスレッドプログラミングの 概略 5
6.
マルチスレッドプログラム ✤ スレッド(
プログラム中の実行の流れ)を 複数もつプログラム ✤ マルチコアCPUの各コア上でスレッドを動かせ ば、同時に複数の処理を実行できる 6
7.
void ThreadProcess1() { doSomething1();
} void ThreadProcess2() { doSomething2(); } int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess2); th1.join(); th2.join(); } 複数の実行の流れ 7
8.
void ThreadProcess1() { doSomething1();
} void ThreadProcess2() { doSomething2(); } int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess2); th1.join(); th2.join(); } 複数の実行の流れ 8
9.
void ThreadProcess1() { doSomething1();
} int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess1); th1.join(); th2.join(); } 複数の実行の流れ 9 同じ関数を渡してスレッドを作成すると
10.
void ThreadProcess1() { doSomething1();
} 一つ関数が、2つのスレッドで別々に同時に実行される int main() { std::thread th1(ThreadProcess1); std::thread th2(ThreadProcess1); th1.join(); th2.join(); } 複数の実行の流れ 10
11.
ゲーム & マルチスレッド
✤ 非同期処理のため ✤ パフォーマンス向上のため 11
12.
スレッドによる非同期処理 ✤ プログラムの中に別の実行の流れを作り、元々の
流れを止めずに処理を行う ✤ ファイルIO/ネットワークIO ✤ 時間のかかる処理を別のスレッドで動かして、UIの流れを止めないようにする ✤ 音楽の再生 ✤ ディスプレイの更新とは別のタイミングで発生するデバイスの要求に 迅速に対応する 12
13.
スレッドによるパフォーマンス向上 ✤ マルチコアCPUの余っているコアを使って処理を
行う ✤ パス探索/AI思考ルーチン ✤ マルチスレッド化の方針 ✤ タスク並列 ✤ 機能ごとに処理を分割し、マルチスレッドで実行する ✤ データ並列 ✤ 単一の機能で処理するデータを分割し、マルチスレッドで実行する ✤ http://www.isus.jp/article/game-special/designing-ai-for-games-4/ 13
14.
マルチスレッドの難点 ✤ スレッド間の同期処理/排他制御
✤ シングルスレッドのプログラミングよりも難くなる ✤ データ競合/デッドロック ✤ 思うようにパフォーマンスが向上しないことも ✤ 処理を分割する粒度、排他制御の仕方が悪ければ逆に パフォーマンスを下げてしまう 14
15.
既存スレッドライブラリの利用 ✤ マルチスレッドプログラミングは、複雑になりや
すく、並行処理にバグがあると発見が困難になる ✤ なので充分に検証されたより高級な仕組み(ライ ブラリや言語拡張)が存在している場合は、それ を使うほうが安心 ✤ TBB, PPL, Parallel STL, OpenMP, ... ✤ ただし、このような仕組みを利用するには実行 ファイルの他にランタイムのDLLが必要になった りすることがある 15
16.
C++のスレッドライブラリ 16
17.
C++のスレッド 17 ✤
(C++11という規格から) 標準規格にスレッドが定義され、 ✤ スレッドを扱うクラスや、マルチスレッドプログ ラミングを支援するクラスが用意された
18.
以前のC++ ✤ 標準規格にスレッドが定義されていなかったの
で、C++の実装系やOSの定義をもとにマルチス レッドプログラムを書く必要があった。 ✤ 標準規格にライブラリも用意されていなかったの で、OSが用意しているスレッドライブラリや pthread, Boost.Threadなどを利用していた ✤ 現在でも、C++11準拠度の低いC++実装系を使用する場 合は、これらのライブラリを使用することになる 18
19.
標準規格に定義されたクラス ✤ std::thread
✤ std::mutex ✤ std::lock_guard/std::unique_lock ✤ std::condition_variable ✤ std::promise/std::future ✤ std::atomic etc,... 19
20.
std::thread ✤ スレッドクラス
✤ スレッドを表し、管理する ✤ スレッドを作成する ✤ スレッドの終了を待機する ✤ スレッドを切り離す 20
21.
スレッドの作成 ✤ std::threadクラスのコンストラクタに、関数や
関数オブジェクトを渡すと、 ✤ 新たにスレッドが作成され、 ✤ コンストラクタに渡した関数がそのスレッド上で 実行される 21
22.
// 別スレッドで呼び出したい関数 void
ThreadProcess() { std::cout << "Hello Thread World" << std::endl; } void foo() { // スレッドを起動 std::thread th(ThreadProcess); th.join(); } スレッドの作成 22
23.
様々な方法でスレッドを作成する 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
24.
// 関数呼び出し演算子を持つクラスのオブジェクト //
(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
25.
スレッドを作成する struct ClassX
{ void Process() {} }; ClassX x; // メンバ関数のアドレスとオブジェクトを渡すと、 // これを結びつけて(bindして) // スレッドを起動できる std::thread th7(&ClassX::Process, x); 25
26.
void DoSomething() {
//時間のかかる関数 Sleep(1000/*millisec*/); } void foo() { std::thread th(DoSomething); //スレッドを起動して //スレッドの終了を待機する th.join(); std::cout << "joined." << std::endl; } スレッドの終了を待機する 26 1秒後に`joined.`が出力される
27.
スレッドを切り離す void bar()
{ std::thread th(DoSomething); // スレッドを切り離し th.detach(); std::cout << "detached." << std::endl; } // thが破棄されてもスレッドは動き続ける 27 即座に`detached.`が出力される
28.
std::threadクラスの注意点 ✤ なにかスレッドを作成した後は、そのスレッドを
管理しているstd::threadクラスのオブジェクトが デストラクトされる前に、join() or detach()を 呼び出す必要がある ✤ 予期せぬバグや、パフォーマンス上の問題となりうるため 28 ✤ ムーブによって所有権を移動した場合は、移動先のオブ ジェクトで適切にjoin() or detach()する
29.
std::mutexクラス ✤ 排他制御(Mutual
Exclusion : 相互排除)を行 うクラス 29
30.
排他制御 30 ✤
データ競合を防ぐための仕組み ✤ デッドロックに注意する
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); 31
32.
排他制御していないコード 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.
排他制御について ✤ 複数のスレッドから同じメモリ領域に
同時にアクセスする場合の安全性 ✤ 読み込み/読み込み → 安全 ✤ 書き込み/書き込み → 未定義動作 ✤ 読み込み/書き込み → 未定義動作 33 詳しくは http://yohhoy.hatenablog.jp/entry/2013/12/15/204116
34.
排他制御していないコード int counter
= 0; void DoWork() { DoSomething(); ++counter; 00A34E2E call DoSomething (0A310FFh) 00A34E33 mov eax,dword ptr ds:[00A41390h] 00A34E38 add eax,1 00A34E3B mov dword ptr ds:[00A41390h],eax (※コンパイラやコンパイルオプションによって 生成されるアセンブラは異なるのでこれは一例) } void Worker() { for(int i = 0; i < 1000; ++i) { DoWork(); } } std::thread th1(Worker); std::thread th2(Worker); 34
35.
データ競合 ✤ 複数のスレッドで共有しているデータを同時に
変更すると、プログラムの整合性が保てなくなる ✤ これをデータ競合(Data Race)と呼ぶ 35
36.
クリティカルセクション ✤ 複数のスレッドから同時にアクセスされてはなら
ない領域 ✤ ここをMutexで排他制御し、整合性を保つように する 36
37.
クリティカルセクション 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
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); 38
39.
クリティカルセクション 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.
アトミック性 ✤ 排他制御されたクリティカルセクションは、他の
スレッドからはアトミック(不可分)に実行され たように見える ✤ 複数の状態を変更しつつ、その途中の状態を他の スレッドから観測されたくない場合は、その範囲 を排他制御によってアトミックにする 40
41.
struct Statistics {
void AddData( int data ); int GetAverage() const; }; Statistics st; void AddTwoData() { st.AddData(10); st.AddData(20); } void DisplayAverage() { ここまで実行された状態で DisplayAverage()を呼び出すと std::cout << st.GetAverage() << std::endl; } 排他制御していないコード 41 GetAverage()は10を返す
42.
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 シングルスレッドプログラミング では観測されない状態
43.
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の中途半端な状態 が観測されないようになる
44.
デッドロック ✤ 2つ以上のスレッドが、お互いのロックを確保し
ようとして処理が停止してしまう状態 ✤ これをデッドロックと呼ぶ ✤ この状態に陥ったスレッドは、回復することも、 自らスレッドを終わらせることもできなくなる 44
45.
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つのスレッドがそれぞれ 最初のロックを確保すると
46.
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 どちらのスレッドも 処理を進められなくなる
47.
デッドロック ✤ C++標準規格のstd::mutex型では、実装系で
可能な場合はresource_deadlock_would_occur をエラーコードに設定したstd::system_error 例外を送出してくれるかもしれない。 ✤ ただしそれに頼るべきではなく、根本的に ロジックの見直しをするべき 47
48.
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 複数のロックを確保する場合は、 必ず同じ順番で確保するようにする
49.
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(); } デッドロック あるいはstd::lock()関数を使用する http://d.hatena.ne.jp/melpon/20121006/1349503776 49
50.
boost::shared_mutex 50 ✤
Reader/Writerロックを実現する ✤ ある変数を保護するための排他制御を行いたい 時、その変数へのアクセスのほとんどが読み込み で書き込みが少ない時に使用する ✤ まだ標準規格には取り入れられていない
51.
reader側 boost::shared_mutex mtx;
std::string text; // 複数のスレッドで共有するデータ void reader() { for( ; ; ) { mtx.shared_lock(); std::string tmp = text; mtx.shared_unlock(); DoSomething(tmp); } } shared_lockは複数のスレッドで std::thread reader_thread1(reader); std::thread reader_thread2(reader); //... 51 同時に取得できる
52.
void writer() {
for( ; ; ) { writer側 std::string const tmp = GetNewText(); mtx.lock(); text = tmp; mtx.unlock(); } } std::thread writer_thread1(writer); std::thread writer_thread2(writer); //... 通常のロックは一つのスレッドしか 52 取得できない
53.
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
54.
lock()/unlock()の問題点 int counter
= 0; std::mutex mtx; void DoWork() { DoSomething(); mtx.lock(); ++counter; // ←ここで複雑なことをして例外が // 発生したり、 mtx.unlock(); // ←ここでunlock()を忘れると、 // ロックが確保されたままになる } // この例だと、次回の呼び出しでデッドロックする 54
55.
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 このようなクラスがあると何が可能になるか
56.
int counter =
0; std::mutex mtx; void DoWork() { DoSomething(); // ロック対象の型をテンプレート引数に指定して // コンストラクタに対象のオブジェクトを渡すと lock_guard<std::mutex> lock(mtx); ++counter; } lock_guardを使用する 56
57.
int counter =
0; std::mutex mtx; void DoWork() { DoSomething(); // コンストラクタ内で自動的にmtxのロック確保 lock_guard<std::mutex> lock(mtx); ++counter; // スコープを抜ける時にlock変数のデストラクタが // 呼ばれ、ロックが解放される } lock_guardを使用する 57
58.
int counter =
0; std::mutex mtx; void DoWork() { DoSomething(); lock_guard<std::mutex> lock(mtx); ++counter; } lock_guardを使用する 58 Mutexのロックをオブジェクトの 寿命に紐付けて管理できる
59.
std::lock_guard 59 ✤
このロックの仕組みを実装したクラス ✤ テンプレート引数には、BasicLockable要件を満 たす型を指定できる ✤ つまり、std::mutexに限らずlock()/unlock()メンバ関数を 持つ型ならなんでもいい ✤ スコープ内でlock_guardクラスのオブジェクト が生きている間、ロックを確保する
60.
std::unique_lock 60 ✤
std::lock_guardクラスの機能に加えて、より高 機能な仕組みをサポートする ✤ 明示的にロックを解放する ✤ ロックの所有権をムーブする ✤ ロックの確保を試行し、成功か失敗かを取得する など ✤ スコープ内で単純なロックをかけたい場合は lock_guardを ✤ ロックをより柔軟に管理したい場合は unique_lockを使用するとよい
61.
std::condition_variable ✤ 条件変数と呼ばれる機能を実装したクラス
✤ 条件変数はモニタという同期の手法を実現する ✤ 条件を満たすまで実行をブロックし、条件を満た した時に実行を再開する 61
62.
ユースケース ✤ Thread1は、データの準備が完了するのを待機
✤ Thread2は、データを準備し、完了したら Thread1に通知 62
63.
std::condition_variable cond; std::mutex
mtx; bool is_data_ready = false; // Thread1 void WaitForDataToProcess() { std::unique_lock<std::mutex> lock(mtx); // データが準備できるまで待機する cond.wait(lock, [&]{ return is_data_ready; }); // データが準備できたので処理開始 process_data(); } condition_variableの使用例 63
64.
// Thread2 void
PrepareDataForProcessing() { retrieve_data(); prepare_data(); // データを準備する boost::lock_guard<boost::mutex> lock(mtx); // データが準備できたら完了フラグをセットして is_data_ready = true; // 待機状態のスレッドを起こす cond.notify_one(); } condition_variableの使用例 64
65.
Producer/Consumerパターン ✤ 非同期処理のデザインパターンの一つ
✤ 条件変数を利用して実装できる 65
66.
データを追加するデータを取り出す 66 Producer/Consumerパターン
Producer1 Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4 キュー
67.
67 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4 キューが満杯だと データを追加できない → キューが空くまで待機
68.
68 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4 データが取り出されると スペースが空く
69.
69 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4 空いたスペースに データを追加
70.
70 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4
71.
71 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4 キューが空だと データを取り出せない → データが追加されるまで待機
72.
72 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4 データを追加すると キューが空でなくなる
73.
73 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 追加されたデータをConsumer4 取り出す
74.
74 Producer/Consumerパターン Producer1
Consumer1 Producer2 Producer3 Producer4 Producer5 Consumer2 Consumer3 Consumer4
75.
template<class T> class
LockedQueue { void enqueue(T val) { if(キューが満杯) { 待機; } // (1) キューの末尾にvalを追加; キューが空でなくなったら(2)に通知; } T dequeue() { if(キューが空) { 待機; } // (2) キューの先頭からデータを取り出し; 満杯だったキューが空いたら(1)に通知; } }; 擬似コード 75 enqueue用/dequeue用に それぞれ一つずつ条件変数を使う
76.
std::promise/std::future ✤ Promiseパターンを実装したクラス
✤ あるタイミングで行った処理の結果を、 別のタイミングで取得するための仕組み ✤ マルチスレッドに限らず、非同期処理のための 仕組み 76
77.
std::promise/std::future // 値をセットするためのpromiseクラスを用意
std::promise<int> p; // promiseクラスのオブジェクトから、 // 対応するfutureクラスのオブジェクトを作成 std::future<int> f = p.get_future(); // pとfは"Shared State"という状態を共有しており、 // これを通じてpからfへ値や例外を受け渡せる 77
78.
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
79.
スレッドを使用しない非同期処理 int main()
{ foo(); // DoProcess(); std::cout << bar() << std::endl; //結果を取得 } 79
80.
int main() {
std::thread th(foo); // 別スレッドで処理 std::cout << bar() << std::endl; //結果を取得 th.join(); } スレッドを使用して非同期処理 80
81.
std::atomic ✤ アトミック変数を実装したクラス
✤ アトミック変数へのアクセスは複数のスレッドか ら同時に行っても安全 81
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); 82
83.
ロックしなくても安全 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になる
84.
ロックしなくても安全 std::atomic<int> counter(0);
void DoWork() { DoSomething(); ++counter; } void Worker() { 011A57B4 lock xadd dword ptr [ecx],eax (※コンパイラやコンパイルオプションによって 生成されるアセンブラは異なるのでこれは一例) for(int i = 0; i < 1000; ++i) { DoWork(); } } std::thread th1(Worker); std::thread th2(Worker); 84
85.
std::atomic ✤ アトミック変数によって、ロックを使用せずに
マルチスレッドプログラムを記述できる ✤ これを利用したアルゴリズムは ロックフリーアルゴリズムと呼ばれる ✤ 詳しくは「プログラミングの魔導書 vol.3」参照 85
86.
std::atomic使用上の注意 ✤ アトミック変数は、正しく使うのが難しいので
注意 ✤ ハードウェアアーキテクチャのメモリモデルを理解してい なければ、思わぬバグの原因となる ✤ 「リリースビルドだけでなぜか落ちる」 ✤ なので、同期処理に不安がある時はミューテック スでちゃんとロックを掛けて処理した方が良い 86
87.
実践「task_queueクラス」 87
88.
実践 ✤ ここまでに紹介した機能を使って、
マルチスレッドをサポートした タスクキュークラスを作成する 88
89.
task_queueクラス 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; 89
90.
1.関数を追加3. タスクを実行 90
task_queueの動作 タスクキュー のスレッド1 ユーザー側の スレッド1 キュー タスクキュー のスレッド2 タスクキュー のスレッド3 タスクキュー のスレッド4 ユーザー側の スレッド2 ユーザー側の スレッド3 ユーザー側の スレッド4 task_queueクラス 2.「タスク」 として保持
91.
//! タスクキューで扱うタスクを表すベースクラス 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
92.
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
93.
// 続き void
process() { for( ; ; ) { // タスクが積まれていたら、 // キューから取り出す std::unique_ptr<Task> task = locked_queue_.dequeue(); task->run(); // F(Args...)を呼び出し // promiseに値を設定する } } // 各スレッドがprocess()を実行する std::vector<std::thread> threads_; }; 93
94.
実装したソース ✤ 今回の発表用のサンプルソースのtask
ディレクトリ ✤ 機能が追加されているので、前述の実装イメージ よりも複雑になっているが、やっていることは ほぼ同じ 94
95.
task_queueを使用 ✤ デモ(サンプルゲーム)
✤ 複数のパーティクルの移動(計算負荷を高くして ある)にtask_queueクラスを使用し、並列処理 をする ✤ サンプルゲームは急ごしらえのため、 ソースの品質についてはご容赦ください 95
96.
まとめ ✤ C++に標準で用意されたクラスを利用してマルチ
スレッドプログラムを作成できる 96 ✤ マルチスレッドによって、パフォーマンスを向上 できる ✤ マルチスレッドプログラミングでは、 データ競合/デッドロックに注意する (そのための仕組みも用意されている)
97.
参考文献 ✤ 「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
Download now