メインスレッドがほぼ瞬時にUIを更新し、他のスレッドがネットワーク経由でデータをポーリングしているか、ジョブの完了に5〜10秒かかることが保証されているGUIアプリケーションのようなものを考えてみましょう。
私はこれについて多くの異なる回答を受け取りましたが、統計的に不可能の競合状態であればまったく心配しないと言う人もいれば、10でもあると言う人もいます。-53競合状態が原因で発生するいくつかのブードゥーマジックの%(数字ではありませんが、これは聞いたことです)、常にそれを必要とするスレッドのロックを取得/解放します。
あなたの考えは何ですか?このような統計的に不可能な状況で競合状態を処理することは、プログラミングの良い習慣ですか?または、読みやすさを妨げるためにコード行を追加することは完全に不必要であるか、逆効果でさえありますか?
それが本当に1の10 ^ 55イベントである場合、それをコード化する必要はありません。これは、1秒間に100万回操作を実行した場合、3 * 10 ^ 41年ごとに1つのバグが発生することを意味します。これは、おおよそ、宇宙の年齢の10 ^ 31倍です。宇宙の1兆兆兆年ごとに1回だけアプリケーションにエラーがある場合は、おそらく十分な信頼性があります。
ただし、エラーが発生する可能性が非常に低いとは考えられません。エラーが発生する可能性がある場合、少なくともたまに発生することはほぼ確実であり、最初から正しくコーディングする価値があります。さらに、スレッドを適切にコーディングして、ロックを適切に取得および解放できるようにすると、コードは将来さらに保守しやすくなります。変更を加えるときに、すべての潜在的な競合状態を再分析し、確率を再計算し、それらが再発しないことを確認する必要があることを心配する必要はありません。
費用対効果の観点からは、十分な利益が得られる場合にのみ、追加のコードを記述する必要があります。
たとえば、間違ったスレッドが「レースに勝った」場合に発生する最悪の事態が情報が表示されず、ユーザーが「更新」をクリックする必要がある場合は、競合状態を回避する必要はありません。たくさんのコードを書くことは、重要でない何かを修正する価値はありません。
一方、競合状態により銀行口座間の送金が不正確になる可能性がある場合は、この問題を解決するために必要なコードの量に関係なく、競合状態から保護する必要があります。
競合状態を見つけることは難しい部分です。おそらく、この質問を書くのに、それを修正するのと同じくらいの時間を費やしたでしょう。可読性が大幅に低下するようなものではありません。プログラマーexpectこのような状況で同期コードを確認すると、実際に無駄になる可能性がありますmoreなぜそこにないのか、それを追加すると関連のないバグが修正されるのか疑問に思います。
確率に関する限り、あなたは驚かれることでしょう。昨年、数千回の自動試行では再現できない競合状態のバグレポートがありましたが、one system of oneの顧客は常にそれを目にしました。 5分をかけて今すぐ修正するというビジネス上の価値と、顧客のインストールでの「不可能」なバグのトラブルシューティングを行う可能性があるというビジネス上の価値により、選択は非常に簡単になります。
ロックを取得して解放します。確率が変わり、アルゴリズムが変わる。それに入るのは悪い習慣です、そして何かがうまくいかないとき、あなたは止まってオッズが間違っているのかどうか疑問に思う必要はありません...
他のスレッドがネットワークまたはデータをポーリングしていて、ジョブの完了に5〜10秒かかることが保証されています。
誰かがパフォーマンスを改善するためにキャッシング層を導入するまで。突然、他のトレッドがほぼ瞬時に終了し、競合状態が頻繁に現れます。
正確にこれが数週間前に発生した場合、バグを見つけるのに約2日間の開発者日がかかりました。
Always認識した場合は競合状態を修正します。
シンプルvs正しい。
多くの場合、単純さが正しさよりも優先されます。それはコストの問題です。
また、競合状態は単純な統計に従わない傾向がある厄介なものです。他の見たところ無関係な同期が原因で競合状態が突然半分の時間発生するまで、すべてがうまくいきます。もちろん、ログをオンにするか、コードをデバッグしない限り。
競合状態(トリッキーになる可能性があります)を防止する実用的な代替手段は、それを検出してログに記録することです(ハードと早期の失敗に対するボーナス)。それが起こらなければ、あなたは少しも失った。それが実際に発生する場合、それを修正するために余分な時間を費やすための確かな正当化を得ました。
競合状態がセキュリティに関連している場合は、それを防ぐために常にコーディングする必要があります。
一般的な例は、UNIXでファイルを作成/開くときの競合状態です。状況によっては、競合状態のプログラムが、システムデーモンプロセスなどのユーザーとやり取りするユーザーよりも高い権限で実行されている場合、権限昇格攻撃につながる可能性があります。さらに悪いことに、カーネル。
競合状態が発生する可能性が10 ^(-80)のようなものであってもランダムにである場合でも、決定的な攻撃者がそのような状態を意図的かつ人為的に作成する可能性は十分あります。
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%が得られたわけではなく、自分の個人的な信頼性の見積もりではかなりの数パーセントを取り戻したことを覚えています。
読みやすさを妨げるためにコード行を追加することは、まったく不必要でしょうか、それとも逆効果でしょうか?
シンプルさは、それが正しい場合にのみ有効です。このコードは正しくないため、将来のプログラマwillは、関連するバグを探すときに必然的にそれを調べます。
それをどのように処理しても(それをログに記録するか、文書化するか、ロックを追加します-これはコストに依存します)、コードを見るときに他のプログラマーの時間を節約できます。
はい、予期しないことを期待してください。私は(他の人のコードでは^^)発生してはならない状態を追跡するのに何時間も費やしました。
常にelseがある、常にデフォルトのケースがある、変数を初期化する(はい、本当に..これからバグが発生する)、ループごとに反復ごとに再利用される変数がないか確認するなど。
特にスレッドの問題が心配な場合は、ブログ、記事、本を読んでください。現在のテーマは不変のデータのようです。
修正するだけです。
私はまさにこれを見てきました。 1つのスレッドは、複雑なデータベースのルックアップを実行し、他のスレッドが次のコード行に到達する前に応答するサーバーに対してネットワーク要求を行うことを管理します。それが起こります。
どこかの顧客が、遅いスレッドを実行したまま、「高速」スレッドのすべてのCPU時間を消費する何かを実行することをいつか決めてしまい、残念です:)
これはコンテキストに依存します。そのカジュアルなiPhoneゲームなら、おそらくそうではないでしょう。おそらく次期有人宇宙船の飛行制御システムでしょう。それはすべて、それを修正するための推定コストに対して測定された「悪い」結果が発生した場合の結果が何であるかに依存します。
これらのタイプの質問はnotプログラミングの質問であるため、経済的な質問であるため、「1つのサイズですべてに当てはまる」という回答はほとんどありません。
ありそうもない競合状態を認識した場合は、少なくともそれをコードに記録してください!
編集:可能な限り修正することを追加する必要がありますが、上記の執筆時点では、少なくともコード内の問題を文書化する他の回答はありません。
どうやってそれが起こるのかをあなたがすでに知っているなら、それに対処するかもしれないと思います。大量のリソースを消費しない場合です。
それはすべて、競合状態の結果が何であるかに依存しています。私はあなたの質問に答える人々が彼らの仕事のラインに正しいと思います。鉱山はルーター構成エンジンです。私にとって、競合状態は、システムが成功したと言っていても、システムを静止させるか、破損させるか、構成解除します。私は常にルーターごとにセマフォを使用しているため、手作業でクリーンアップする必要はありません。
私のGUIコードの一部は、競合状態が発生したためにユーザーにエラーが発生する可能性があるような競合状態が発生しやすいと思いますが、データの破損や誤動作の可能性がある場合は、そのような可能性はありません。そのようなイベントの後のアプリケーション。
おかしなことに、最近この問題に遭遇しました。私の状況ではレースコンディションが可能であることにさえ気づいていませんでした。競合状態は、マルチコアプロセッサが標準になったときにのみ現れました。
シナリオはだいたいこんな感じでした。デバイスドライバーが、ソフトウェアが処理するイベントを発生させました。制御は、デバイスのタイムアウトを防ぐために、できるだけ早くデバイスドライバーに戻る必要がありました。これを確実にするために、イベントは記録され、別のスレッドにキューイングされました。
_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)
を問題のある場所に置きました。以来問題はありません。