ほぼ確実に発生する可能性のない競合状態を処理する必要がありますか?


52

メインスレッドがUIをほぼ瞬時に更新し、他のスレッドがネットワーク経由でデータをポーリングしているGUIアプリケーションや、ジョブの完了に5〜10秒かかることが保証されているGUIアプリケーションのようなものを考えてみましょう。

私はこれについて多くの異なる回答を受け取りましたが、統計的不可能の競合状態である場合、それをまったく心配しないと言う人もいますが、10-53%さえあればあなたは数字ではなく、これは私が聞いたものです)競合状態のために起こっているブードゥー教の魔法、常にそれを必要とするスレッドのロックを取得/解放します。

あなたの考えは何ですか?このような統計的に不可能な状況で競合状態を処理するのは、プログラミングの良い習慣ですか?または、読みやすさを妨げるためにコードの行を追加することはまったく不要であるか、逆効果になるでしょうか?


21
人々がそのようなチャンスを述べているとき、なぜその番号を述べる人の教育についてだれも尋ねないのですか?そのような数字でバックアップする前に、統計の正式な教育が必要です。
ピーターB

27
物理学者として、p <1E-140はp = 0を意味します。この宇宙では起こりません。0.00000000000000000000000000000000000000000000000000001%はずっと大きいです。
–MSalters

15
この競合状態によって、誰かが喜んでアプリをクラッシュさせないようにしてください。これがセキュリティ問題の原因である可能性があります。
-toasted_flakes

27
100万分の1の確率は、10回のうち9回発生します。
カズドラゴン

27
「ほぼ確実に発生する可能性はありませんか?」午前3時に本番環境で発生し、非常に高価になる可能性が高いことを意味します。

回答:


137

本当に10 ^ 55のイベントである場合は、コーディングする必要はありません。つまり、操作を1秒間に100万回実行すると、3 * 10 ^ 41年ごとに1つのバグが発生します。これは、およそ宇宙の年齢の10 ^ 31倍です。アプリケーションが宇宙の兆兆兆年ごとに1回だけエラーを持っている場合、それはおそらく十分に信頼できます。

ただし、エラーが発生する可能性はそれほど高くないことを非常に強く主張します。エラーを思い付くことができれば、それが少なくとも時々起こることがほぼ確実であるため、最初から正しくコーディングする価値があります。さらに、ロックを適切に取得および解放するように、最初にスレッドを正しくコーディングすると、コードの将来のメンテナンス性が大幅に向上します。潜在的なすべての競合状態を再分析し、それらの確率を再計算し、それらが再発しないことを保証する必要があることを変更するときに心配する必要はありません。


66
私は何年も前に読んだコメントを思い出しますが、今では「100万分の1のチャンスは通常来週の火曜日です」とは言えません。+1「それはありそうもない」と言って。
ベヴァン

2
賭けの+1。競合状態に対処する最善の方法は、それらを取り除くことです。
-Blrfl

10
@Bevan「100万分の1は通常次の火曜日です」...宝くじをしている場合を除きます:)
dasblinkenlight

22
@dasblinkenlightしかし、ほとんどの宝くじで誰かが勝つ可能性は100%に近づきます。誰を予測する、これが課題です。
ベヴァン

3
@Bevan:そのコメントは、質問を読んだときにまさに頭に浮かんだものでした-ここにリファレンスがあります:blogs.msdn.com/b/larryosterman/archive/2004/03/30/104165.aspx
Doc Brown

69

費用対効果の観点から、十分な利益が得られた場合にのみ追加のコードを記述する必要があります。

たとえば、間違ったスレッドが「レースに勝った」場合に発生する最悪の事態が、情報が表示されず、ユーザーが「更新」をクリックする必要がある場合、競合状態に対する保護を気にしないでください。多くのコードを書くことは、取るに足らないものを修正する価値はありません。

一方、競合状態により銀行口座間で不正確な送金が発生する可能性がある場合は、この問題を解決するためにどの程度のコードを記述する必要があるとしても、競合状態を防止する必要があります。


20
+1:「失敗のように見える失敗」と「成功のように見える失敗」を区別するため。ドメインによっては、誤った情報ははるかに深刻です。
-deworde

2
+1は、競合状態の結果がどうなるかを大きく変えます。
付与

+1競合状態の結果は、対処する必要があるかどうかの主要な決定要因になるはずです。飛行機のcrash落を引き起こす可能性のある競合状態は、ユーザーにアプリケーションの再起動を強制する可能性のある状態とは大きく異なります。
突く

1
+1:結果はおそらくあなたが分析すべきものであり、それが起こる確率ではないと言うでしょう。結果が重要でない場合、非常に一般的であっても、競合状態を処理する必要はないかもしれません。
レオ

1
しかし、競合状態を自動的に修正することは、より多くのコードを記述する必要があることを意味しないと考えてください。バグのあるコードの大きな塊を削除し、正しいコードの小さな塊に置き換えることも意味します。
JesperE

45

競合状態を見つけることは難しい部分です。おそらく、この質問を書くのにあなたがそれを直さなければならなかったのとほぼ同じ時間を費やしたでしょう。それはそれがはるかに読みにくくなるようではありません。プログラマー、このような状況で同期コードを確認すること期待しており、実際に同期コードが存在しない理由や、追加すると関連のないバグが修正されるかどうかを疑問視する時間を浪費する場合があります。

確率に関する限り、驚くでしょう。昨年、数千の自動試行で再現できない競合状態のバグレポートがありましたが、1人の顧客の1つのシステムが常にそれを確認していました。今すぐ修正するのに5分を費やすビジネス上の価値と、顧客のインストールで「不可能な」バグのトラブルシューティングを行う可能性があるため、選択は簡単です。


1
これも!他のプログラマーがコードを読むときに起こりうる問題について熟考することを避けるために、必要なことを行います(失敗する可能性が「低い」場合でも)。
ケーシーKuball

「今すぐ修正するのは、後で修正するよりも速くて安価です」という点を十分に考慮してください。
iconoclast

2
1競合状態の確率は、おそらくそれがでそうに見えるそうしても、多くの要因に依存していることを指摘して、あなたのそれは、顧客のシステム上でより頻繁に発生する可能性があり、コンフィギュレーション/異なるOS上の/などの次のリリースで
sleske

27

ロックを取得して解放します。確率が変わり、アルゴリズムが変わります。入るのは悪い習慣であり、何かがうまくいかないとき、あなたは立ち止まって、あなたがオッズを間違えたかどうかを心配する必要はありません...


6
アルゴリズム変更の場合は+1。現時点では、競合状態を認識している場合、確率は低くなります。1年後、競合状態を忘れたときに、コードの変更を行うと、バグのタイミングと確率が大幅に変わる可能性があります。
フィル

13

他のスレッドがネットワーク経由でデータをポーリングしているか、ジョブの完了に5〜10秒かかることが保証されています。

誰かがパフォーマンスを改善するためにキャッシングレイヤーを導入するまで。突然、他のトレッドがほぼ瞬時に終了し、競合状態が頻繁に現れます。

数週間前にまさにこれが起こっていた場合、バグを見つけるのに約2日間かかりました。

競合状態を認識した場合は、常に修正してください。


8

シンプルvs正しい。

多くの場合、単純さは正確さよりも優先されます。これはコストの問題です。

また、競合状態は、単純な統計に従わない傾向がある厄介なものです。無関係と思われる他の同期によって、半分の時間で競合状態が突然発生するまで、すべてがうまくいきます。もちろん、ログをオンにするか、コードをデバッグしない限り。

競合状態を防止するための実用的な代替手段(難しい場合があります)は、それを検出してログに記録することです(ハードに早期に失敗した場合のボーナス)。それが起こらない場合は、ほとんど失われません。それが実際に起こった場合、あなたはそれを修正するために余分な時間を費やすための確固たる正当化を得ました。


1
ロギングのために+1し、完全に修正するのが複雑すぎる場合は早期に失敗します。
マーティンBa

多くの場合、単純さは完全性よりも優先されます。これらのケースでは同期はほとんどありません。それはほとんど常にあなたに噛み付くように戻ってきます(またはあなたのコードを維持することを任された貧しい人)後で。
reirab 14

@reirab私は同意しません。まれなイベントを考慮する場合、ログに記録された障害は費用対効果が高くなります。例:ユーザーが正確な月の遷移(1/31 23:59:00-> 2/1 00:00:00)でネットワークを切り替えている場合、電話アプリの障害率が1/100(クラッシュ)である場合、おそらくそれについて聞かないでしょう。しかし、その後、サーバー上の接続で1/10 ^ 9のクラッシュの可能性は受け入れられません。場合によります。
ptyx 14

7

競合状態がセキュリティ関連である場合、それを防ぐために常にコーディングする必要があります。

一般的な例は、UNIXでファイルを作成/開く場合の競合状態です。競合状態のプログラムが、システムデーモンプロセスなどのユーザーと対話するユーザーよりも高い特権で実行されている場合、状況によっては特権エスカレーション攻撃につながる可能性がありますさらに悪いことに、カーネル。

競合状態に10 ^(-80)の確率がランダムに発生する場合でも、決意のある攻撃者がそのような状態を意図的かつ人為的に作成する可能性が高い場合があります。


6

Therac-25!

Therac-25プロジェクトの開発者は、治療用XRAYマシンでのUIとインターフェイス関連の問題との間のタイミングについてかなり自信を持っていました。

あるべきではありませんでした。

この有名な生死のソフトウェア災害についての詳細は、次のサイトでご覧いただけます。

http://www.youtube.com/watch?v=izGSOsAGIVQ

または

http://en.wikipedia.org/wiki/Therac-25

アプリケーションは、医療機器よりも故障の影響を受けにくい場合があります。有用な方法は、生産される可能性のあるすべてのユニットについて、製品の寿命にわたる発生の可能性と発生コストの積としてリスクのエクスポージャーを評価することです。

コードを最後までビルドすることを選択した場合(そして、あなたが持っているように聞こえます)、システムの内部または外部のコンピューターが高速になると、数年ごとに数ゼロを簡単に取り除くことができるムーアの法則を考慮する必要があります。何千ものコピーを出荷する場合は、さらにゼロを削除してください。ユーザーがこの操作を毎日(または毎月)何年も実行する場合は、さらにいくつかを削除します。Googleファイバーが利用可能な場所で使用されている場合、それではどうなりますか?UIガベージがGUI操作中に収集される場合、それはレースに影響しますか?GUIの背後でオープンソースまたはWindowsライブラリを使用していますか?更新はタイミングに影響しますか?

セマフォ、ロック、ミューテックス、バリア同期は、スレッド間でアクティビティを同期する方法の1つです。それらを使用していない場合、プログラムを保守している別の人が、スレッド間の関係に関するかなり迅速な仮定を変更し、競合状態に関する計算が無効になる可能性があります。

明示的に同期することをお勧めします。問題が発生することはないかもしれませんが、顧客が発生する可能性があるためです。さらに、たとえ競合状態が発生しなかったとしても、コードを守るためにあなたまたはあなたの組織が裁判所に呼ばれた場合はどうでしょうか(トヨタは数年前にプリウスに関係していたので)。方法論を徹底すればするほど、うまくいくでしょう。「コードが失敗することはわかっているが、この方程式を書き留めて、これが私たちの生涯に起こらないことを示すために」と言うよりも、「このようなありそうもないケースに対してガードする...」と言う方が良いかもしれません。 」

確率の計算は他の誰かからのもののようです。彼らはあなたのコードを知っており、エラーが発生していないことを信頼するのに十分知っていますか?何かに対して99.99997%の信頼性を計算した場合、大学の統計クラスに戻って考えて、常に100%を獲得したわけではなく、自分の個人的な信頼性の推定値のかなりの数を取り下げたことを思い出してください。


1
Therac-25の言及については+1。ここで多くの重要な教訓。
スチュアートマーク

これは良い答えだと思いますが、競合状態を解消できなければ、趣味のGUIプロジェクトが確実に人々を死なせるとは言えないでしょう。
マルクタニ

私は議論することはあまりありませんが、私がいたら、コードを書くときはいつでも正しく書くべきだと主張するかもしれません。コードが単純で、おそらく私たちが唯一の著者である趣味のプロジェクトから競合状態を取得することを練習できれば、複数の著者の作業を統合する必要がある作業プロジェクトに取り組むとき、私たちははるかに準備ができています。
DeveloperDon

4

読みやすさを妨げるためにコードの行を追加することは完全に不要であるか、逆効果ですらありますか?

シンプルさは、それが正しい場合にのみ有効です。このコードは正しくないため、将来のプログラマ関連するバグを探す際に必然的にそれを見るでしょう

記録方法、文書化方法、ロックの追加方法のいずれを使用する場合でも、コストに依存しますが、他のプログラマーがコードを見る時間を節約できます。


3

これはコンテキストに依存します。カジュアルなiPhoneゲームなら、おそらくそうではないでしょう。おそらく、次の有人宇宙船の飛行制御システム。「悪い」結果がそれを修正するための推定コストに対して測定された場合、それは結果が何であるかに依存します。

これらのタイプの質問はプログラミングに関する質問ではなく、経済的な質問であるため、「万能」の回答がめったにありません


3
「次の有人宇宙船の飛行制御システム」間違いなく
-deworde

おそらく...間違いなく...誰がロケットに乗っていたかに依存します:
グランドマスター

3

はい、予期しないことを期待しています。私は何時間も(他の人のコードでは^^)費やして、決して起こらないはずの状態を追跡しました。

常にelseがある、常にデフォルトのケースがある、変数を初期化する(はい、本当に..これからバグが発生します)、各反復で再利用される変数のループをチェックするなど

スレッドの問題が特に心配な場合は、このテーマに関するブログ、記事、および本を読んでください。現在のテーマは不変データのようです。


3

修正するだけです。

私はまさにこれを見ました。1つのスレッドは、他のスレッドが次のコード行に到達する前に、複雑なデータベース検索を実行して応答するサーバーへのネットワーク要求を処理します。それは起こります。

一部のお客様は、遅いスレッドを実行したままで、「高速」スレッドのすべてのCPU時間を消費する何かを実行する日をどこかで決めます。ごめんなさい。


1

ありそうもない競合状態を認識した場合は、少なくともコードに文書化してください!

編集:可能な場合は修正することを追加する必要がありますが、上記の執筆時点では、少なくともコードの問題を明示的に記載した他の回答はありません。


1
ええ、少なくともそれを試して検出し、発生した場合はログに記録します。私見では、すべてのエラーを回避しないことはまったく問題ありません。しかし、少なくとも誰かにそれが発生したことと、それが間違っていないというあなたの仮定が間違っていたことを知らせてください。
スティーブベネット


0

それはすべて、競合状態の結果によって異なります。あなたの質問に答える人々は、彼らの仕事のラインにとって正しいと思います。私のものはルーター構成エンジンです。私にとって、競合状態は、成功したと言っても、システムを静止させたり、破損させたり、構成を解除したりします。ルーターごとにセマフォを常に使用するため、手作業で何かをクリーンアップする必要はありません。

私のGUIコードの一部は、競合状態が発生したためにユーザーにエラーが発生するような競合状態になりやすいと思いますが、データの破損またはそのようなイベントの後のアプリケーション。


0

面白いことに、最近この問題に遭遇しました。私の状況では、競合状態が起こり得ることすら知りませんでした。競合状態は、マルチコアプロセッサが標準になったときにのみ現れました。

シナリオはおおよそこのようなものでした。デバイスドライバーは、ソフトウェアが処理するイベントを発生させました。デバイスのタイムアウトを防ぐために、できるだけ早くデバイスドライバーに制御を戻す必要がありました。これを保証するために、イベントは記録され、別のスレッドでキューに入れられました。

Receive event from device:
{
    Record event details.
    Enqueue event in the queuing thread.
    Acknowledge the event.
}

Queueing thread receives an event:
{
    Retrieve event details.
    Process event.
    Send next command to device.
}

これは何年もうまくいきました。その後、特定の構成で突然失敗します。キューイングスレッドは、シングルプロセッサの時間を共有するのではなく、イベント処理スレッドと完全に並行して実行されるようになりました。イベントが確認される前にデバイスに次のコマンドを送信することができたため、シーケンス外のエラーが発生しました。

1つの構成で1人の顧客にしか影響しないことを考えるとThread.Sleep(1000)、問題があった場所に恥ずかしがり屋を入れました。以来、問題はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.