TL;DR

プログラムの振る舞いが、制御できないイベントのタイミングや順序に左右される状態。起こりうる振る舞いのどれかが望ましくないとき、バグになる。

仕組み

並行に動く処理が、想定しない順序で終わると起きる。典型的な例は、2 つのスレッドが、0 で始まる共有の整数をそれぞれ 1 増やす処理である。読み出しと書き込みが交互に入り組むと、両方のスレッドが 0 を読んで 1 を書くので、期待した 2 ではなく 1 になる。

例

Effective TypeScript 項目47 では、window.user がセットされる前に greetUser を呼び出せてしまうことを、競合状態の例として挙げている。

注意点

共有の状態を扱う処理は、互いに排他的なクリティカルセクションに入れる。ロックやセマフォによる排他制御が、主な防ぎ方である。

データ競合を競合状態の一種とするかは、文献によって異なる。データ競合がなくても、タイミングによる非決定性は起こりうる。

結果が非決定的なので、再現が難しい。デバッグモードで動かしたり、ログを足したり、デバッガをつないだりすると消えてしまうことがあり、こうしたバグはハイゼンバグと呼ばれる。そのため、注意深い設計で競合状態を避けることが勧められている。

関連

出典