細かいこと、例外的なことなどいろいろあると思いますが、簡単にまとめてみました。
スレッド同期とは?
マルチスレッド処理においてスレッド間の処理を同期させるために、スレッドにタイムスライスを与えないこと。(スレッドを眠らせること)
そもそもスレッドって?
プロセス
- アプリケーションの実行中のインスタンス。
- プロセス中には 1 つ以上のスレッドが存在する。
- プロセス起動時に同時に生成されるメインのスレッドをプライマリスレッドという。
- プライマリスレッドの終了 = プロセスの終了。
- メモリはプロセス単位で管理されるので、プロセス内のスレッドは同一メモリ空間を共有する。
スレッド
プロセスの実行の単位。
プロセス空間で、コードを実行する。
マルチスレッド処理で問題になること
スレッドは実行の単位と書きました。
プロセス内に複数のスレッドがある場合、どのスレッド処理も非同期的に実行されます。
このような状況において、複数のスレッドが同一の変数(メモリ領域)に同時にアクセスする場合に問題が起こりえます。
単に複数のスレッドが値を読み出すだけなら、問題は発生しません。
一方、どれかのスレッドが値を変更している最中に、別のスレッドが値を読み出したり、変更しようとすると、アクセス違反が発生します。
このような問題に対処するために、スレッド間で処理の同期をとる必要があり、それをスレッド同期と呼びます。
マルチスレッドを考える
シングルスレッドのプロセスを 2 つ動かしても、1つのプロセスで 2 つスレッドを動かしても、スレッドの数は 2 つ、つまりマルチスレッドですね。
それぞれにどのような違いがあるかを考えてみます。
複数プロセスを起動する
プロセスを 2 つ以上動かす。
スレッド間の連携が必要ないか疎で、プロセスの利用コストが相対的に低い場合はこちらを利用します。
プロセスの利用コスト(メモリのような OS のシステムリソース)はスレッドに比べて高いですが、連携が疎だと同期制御は相対的に簡単です。
プロセス内マルチスレッド
プロセス内で 2 つ以上のスレッドを動かす。
スレッド間でメモリを共有するなど、複数のスレッドが密接に連携する場合はこちらを利用します。
スレッドの連携が密だということは、同期制御は相対的に難しいものになります。
スレッド同期オブジェクト
スレッドの同期を目的としたオブジェクトには以下のようなものがあります。
共有データへのアクセスを直接的に制限するためのもの
クリティカルセクション
- ある共有データに対するアクセスを単一スレッドに制限する。
- 最も使いやすく理解しやすいので、簡単に扱える。
- 同一プロセス内のスレッドの同期しかとれない。
利用例:
- プロセス内のスレッド間での変数(メモリ領域)アクセスの同期をとる。
ミューテックス
- ある共有データに対するアクセスを単一スレッドに制限する。
- 自分を所有するスレッドを覚えている。
- 複数プロセス内のスレッドの同期がとれる。
利用例:
- ミューテックスを使って重複起動のチェックを行う。
ある条件になるまでスレッドを待機させる類のもの
セマフォ
- ミューテックスと似ているが、ある共有データに対して、複数スレッドのアクセスが可能。
- 参照カウンタを持つ。
- 所有権を持つスレッドが管理されていないため、あるスレッドがセマフォを待ちながら、他のスレッドがセマフォを解放できる。
イベント
- 前述のオブジェクトはデータアクセス管理に使われるが、イベントは何らかの処理の通知に使われる。
利用例:
- 前処理が終わるまで(イベントがシグナル状態になるまで)、スレッドを待機させる。
参考リンク
標準 C++
Windows 関連
日経ソフトウェア @ マルチタスクに不可欠な同期の仕組みを学ぶ
Microsoft @ マルチスレッドコードでよく見られる 11 の問題を解決する
同期と直接関連はないけど、SleepEx から制御を戻すためのサンプル
Win32API ユーザーモード非同期プロシージャ呼び出し QueueUserAPC, SleepEx