競合状態:ロック有無で残高が壊れる
2つのスレッドが同じ残高に +10 する。読み→計算→書き戻しの順番(インターリーブ)を自分で並べ替えると、ロック無しでは加算が消える。
ロック(mutex)
100
10
手動でA/Bのステップを交互に踏むと「割り込み」が再現できます。「自動:壊れる順番」は
RA RB WA WB 形式の最悪インターリーブを実行します。
現在の残高(メモリ)
—
正しい期待値
—
消えた入金(lost update)
—
ロック保持
—
共有メモリ(残高セル)と各スレッドのレジスタ
実行タイムライン(上から下へ進行)
残高への入金は、CPU の中では一気には起きません。① 読む(read) → ② 計算する(+10) → ③ 書き戻す(write) の3手に分かれます。
2つのスレッドがこの3手を交互(インターリーブ)に実行すると、両方が「古い同じ値」を読んでしまい、後から書いた方が先の入金を上書きして消す──これが 競合状態(race condition)とロスト・アップデートです。
mutex(ロック)を掛けると、片方が①〜③を終えるまでもう片方は割り込めません(相互排他)。ロックを切り替え、ステップの順番を自分で並べて結果の違いを確かめましょう。
2つのスレッドがこの3手を交互(インターリーブ)に実行すると、両方が「古い同じ値」を読んでしまい、後から書いた方が先の入金を上書きして消す──これが 競合状態(race condition)とロスト・アップデートです。
mutex(ロック)を掛けると、片方が①〜③を終えるまでもう片方は割り込めません(相互排他)。ロックを切り替え、ステップの順番を自分で並べて結果の違いを確かめましょう。
いま何が起きている?
ここがポイント
- 同じ操作でも順序が変われば結果が変わる ──
RA RB WA WBは壊れ、RA WA RB WBは正しい。 - read-modify-write は不可分でない ── 3手の途中で割り込まれるから、両者が古い値を読んで一方の入金が消える。
- mutex は「①〜③を1人で完了するまで他を待たせる」──臨界区間を相互排他にし、他スレッドの同時進入を防ぐ。
- ロック無しの正しさは「たまたま」──運の良い順番なら合うが、保証は無い。バグは再現しにくく恐ろしい。