スレッドセーフとは、複数のスレッドから同時に利用しても、定められた使い方の範囲で正しく動作する性質です。共有データの整合性や、更新内容が他のスレッドに正しく見えることがポイントになります。
まずは「共有カウンタを2つのスレッドが増やす」例で競合を確認し、排他制御・アトミック操作・共有を避ける設計の使い分けを整理しましょう。
なぜ同時アクセスで結果がずれるのか
カウンタが0のとき、スレッドAとBがそれぞれ1回ずつ加算すれば、期待する結果は2です。しかし、カウンタを1増やす処理が「読出し→計算→書込み」に分かれていると、次の順序で更新が失われることがあります。
| 順番 | スレッドA | スレッドB | 共有カウンタ |
|---|---|---|---|
| 1 | 0を読む | — | 0 |
| 2 | — | 0を読む | 0 |
| 3 | 0+1を計算し、1を書く | — | 1 |
| 4 | — | 0+1を計算し、1を書く | 1 |
加算は2回なのに結果は1です。このように実行の順序やタイミングによって結果が変わる問題を競合状態(レースコンディション)と呼びます。単一コアでも、途中でスレッドが切り替われば起こり得ます。
排他制御では「一連の処理」を守る
共有資源にアクセスし、同時実行されると問題になる部分をクリティカルセクションと呼びます。先ほどの例では、読出しから書込みまでを同じロックで保護し、途中に別の更新が入り込まないようにします。
【概念を示す疑似コード】 同じ共有ロックを取得する カウンタを読む 値に1を足す カウンタへ書く 共有ロックを解放する
読出しと書込みを別々にロックするだけでは不十分です。全ての更新経路が同じ保護の仕組みに従う必要があります。実装では例外時もロックを確実に解放し、読み手への可視性も、言語やライブラリの同期規則に従って確保します。
スレッドセーフにする主な方法
| 方法 | 考え方 | 注意点 |
|---|---|---|
| ミューテックスなどで排他制御 | 保護する処理を同時に実行させない | 待ち時間やデッドロックを考慮する |
| アトミック操作 | 対応する更新を、他のスレッドから途中が見えない一つの操作として行う | 複数の操作を組み合わせた全体まで自動的に安全になるとは限らない |
| 不変オブジェクトを使う | 生成後に状態を変えず、更新の競合を避ける | 参照先も含めた不変性と、安全な公開の方法を確認する |
| スレッドごとにデータを分ける | 同じ可変データを共有しない | 後で結果を集約・共有する部分は別途設計する |
スレッドセーフな実装が、必ず他のスレッドをブロックするわけではありません。アトミック操作や、共有する可変データを減らす設計も選択肢です。
「メソッドが安全」でも組合せは要確認
例えば、在庫の取得と減算がそれぞれスレッドセーフでも、「在庫を確認してから購入分を減らす」全体が安全とは限りません。2つのスレッドがどちらも在庫1を確認してから購入を進めると、売り越しが起こり得ます。
在庫確認と減算を同じ保護範囲にまとめるなど、業務上守るべき条件まで含めて考えます。アトミックな減算だけに置き換えても、在庫不足を防ぐ条件判定と一体になっていなければ解決しません。
試験で混同しやすい言葉
- 競合状態:処理の順序やタイミングが結果に影響する問題。
- データ競合:同じメモリへの同期されていない競合アクセスを指す用語。厳密な定義や影響は言語の仕様で確認します。
- 排他制御:共有資源を同時に扱う範囲を制御する手段。
- スレッドセーフ:複数スレッドから利用しても正しく動作する性質。
- デッドロック:互いに必要な資源の解放を待ち、処理が進まなくなる状態。
「スレッドセーフでない=必ず未定義動作」と一律に覚えるのは避けましょう。起こり得る結果は、使用言語・アクセス方法・同期の有無によって異なります。また、整合性を守るだけでなく、ロック順序や処理時間など、処理が進むための設計も必要です。
確認問題
共有カウンタへの加算で更新の消失を防ぐ方針として、最も適切なのはどれでしょうか。
- 読出しだけをロックし、書込みは自由に行う。
- 各スレッドが別々のロックを使う。
- 全ての更新側が共通のロックで、読出し・加算・書込みの一連の処理を保護する。
解答・解説を見る
正解は③です。同じ共有データの更新を、同じロックと適切な範囲で保護する必要があります。①は書込みが競合し、②は互いの進入を防げません。読み手を含めた整合性や可視性の要件も別途確認します。
資源共有から排他制御まで、noteでつなげて復習
「なぜスレッドで共有データの競合が起きるのか」を、プロセスとの違いから整理したい方へ。
関連noteでは、プロセスとスレッド、資源共有、競合、排他制御、セマフォとミューテックス、デッドロックを扱っています。この記事の用語をOS分野全体の理解につなげる復習に使えます。
教材の公開範囲・価格などはリンク先でご確認ください。
まとめ
スレッドセーフは「複数スレッドから使っても正しく動く性質」です。共有する可変データ、競合する操作、守る範囲を順に見つけ、排他制御・アトミック操作・共有を避ける設計を使い分けましょう。
参考:Oracle:Thread Interference/Oracle:Synchronized Methods(Javaの同期と競合の説明)

コメント