【きょうごうじょうたい】

競合状態(レースコンディション) とは?

最終更新:
💡 別々に正しい処理でも、交ざると結果が崩れる

複数の処理の進み方やタイミングによって、本来守るべき結果が崩れる問題。共有値を読んでから更新する間に別の処理が入ると、加算の消失や二重販売などが起きる。守る範囲全体を一つの操作として扱うなど、処理に合った対策が必要。

📌 このページのポイント
競合状態:1回分の加算が消える例共有カウンター:0処理A0を読む → 1を作る1を書き戻す処理B0を読む → 1を作る1を書き戻すこの順序での結果:1期待した値は2両方が0を読んでから、書くと…
共有値を読む・加算する・書く操作を分けた模式例。両方が0を読み、それぞれ1を書いた順序では、最後に1が残ります。
ひよこ ひよこ
どうして足し算の結果が変わるの?
ペンギン先生 ペンギン先生
共有カウンターが0で、AとBがそれぞれ1を足す例を考えよう。両方が先に0を読み、それぞれ1を作って書くと、最後は1になる。本来は2になってほしいのに、1回分が失われるんだ。
ひよこ ひよこ
まったく同じ瞬間に動くと起きる?
ペンギン先生 ペンギン先生
同じ瞬間に実行する必要はないよ。Aが読んだ後、書く前にBも読む、というように処理が交ざることが問題なんだ。実行の順序が違えば、期待した結果になる場合もあるね。
ひよこ ひよこ
Webサービスでも起きる?
ペンギン先生 ペンギン先生
在庫1個をAとBが別々に確認し、どちらも購入を受け付けてしまう例があるよ。チェックと在庫の更新を別々に行うと、その間に他の注文が入れる。確認と更新を守る仕組みが必要なんだ。
ひよこ ひよこ
ロックを付ければ解決できる?
ペンギン先生 ペンギン先生
読み取りから更新まで必要な範囲を守り、競合する処理が同じルールを使うことが大切だよ。DBなら行ロックを使う一つのトランザクション内で確認と更新を行う方法や、条件付きのUPDATEなどがある。単にトランザクションで囲むだけでは、すべての競合を防げないね。
ひよこ ひよこ
テストで見つけられる?
ペンギン先生 ペンギン先生
タイミングによって再現しない場合もあるよ。処理の交ざる順序を考えて試すことが大切。ThreadSanitizerはデータ競合の検出に使えるけれど、在庫のような業務上の競合まで全部見つける道具ではないんだ。
ペンギン
まとめ:ざっくりこれだけ覚えればOK!
「競合状態」って出てきたら「処理のタイミングが交ざって、結果が崩れる問題」と思えばだいたいOK!
📖 おまけ:英語の意味
「Race Condition」 = 競合状態
💬 Raceは競争。複数の処理が進む順序やタイミングによって、共有する結果が変わることを表すよ。

参考資料

← 用語集にもどる