マルチスレッドのアプリケーションを書いていると、よくある問題としてレースコンディションがあります。
私からコミュニティへの質問は
レースコンディションとは何ですか?どうやって検出するのですか?どのように処理するのですか?最後に、レース状態の発生を防ぐにはどうすればよいか?
レースコンディションとは、2つ以上のスレッドが共有データにアクセスできる状態で、同時にデータを変更しようとすることです。スレッドスケジューリングアルゴリズムは、いつでもスレッドを入れ替えることができるため、スレッドが共有データにアクセスしようとする順番はわかりません'。そのため、データの変更結果はスレッドスケジューリングアルゴリズムに依存します。つまり、両方のスレッドがデータへのアクセス/変更を"racing"していることになります。
あるスレッドが"check-then-act"(例:"check"値がXであれば、"act"値がXであることに依存して何かを行う)を行い、別のスレッドが"check"と"act"の間に値に対して何かを行う場合に問題が発生することが多いです。例えば、以下のようになります。
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
要するに、チェックとアクションの間に別のスレッドがxを変更したかどうかによって、yは10にもなるし、何にでもなるということです。それを知る方法はありません。
競合状態の発生を防ぐためには、通常、共有データにロックをかけて、一度に1つのスレッドしかデータにアクセスできないようにします。これは次のような意味です。
// Obtain lock for x
if (x == 5)
{
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
// release lock for x
レースコンディションとは、ある一時的な条件でのみ発生するバグの一種です。
例を挙げます。 AとBという2つのスレッドがあるとします。
スレッドAでは
if( object.a != 0 )
object.avg = total / object.a
スレッドBでは
object.a = 0
スレッドAがobject.aがnullでないことを確認した直後にプリエンプトされた場合、Bはa = 0
を実行し、スレッドAがプロセッサを獲得すると、"divide by zero"を実行します。
このバグは、スレッドAがif文の直後にプリエンプトされた場合にのみ発生し、非常に稀ですが、発生する可能性があります。
レースコンディションとは、並行プログラミングにおいて、2つの並行スレッドやプロセスがリソースを奪い合い、どちらが先にリソースを取得するかによって、結果的に最終的な状態が決まる状況のことです。