9

ミューテックス ロックとセマフォを使用して (特に C++ で) スレッドを処理する場合、デッド ロックを回避し、クリーンな同期を行うための簡単な経験則はありますか?

4

9 に答える 9

16

簡単な経験則として、アプリケーションのどこからでも一貫した予測可能な順序で常にロックを取得することをお勧めします。たとえば、リソースに名前がある場合は、常にアルファベット順にロックしてください。ID が数値の場合は、常に最低から最高の順にロックします。正確な順序または基準は任意です。重要なのは、一貫性を保つことです。そうすれば、デッドロック状態になることはありません。例えば。

  1. スレッド 1 はリソース A をロックします
  2. スレッド 2 はリソース B をロックします
  3. スレッド 1 は B のロックを取得するために待機します
  4. スレッド 2 は、A のロックを取得するために待機します
  5. デッドロック

上記の経験則に従えば、上記のようなことは決して起こりません。より詳細な議論については、食事の哲学者の問題に関するウィキペディアのエントリを参照してください。

于 2009-12-12T07:12:40.513 に答える
7
  1. 可能であれば、一度に複数のミューテックス/セマフォをロックする必要がないようにコードを設計してください。
  2. それが不可能な場合は、常に複数のミューテックス/セマフォを同じ順序でロックしてください。したがって、コードの一部がミューテックス A をロックしてからセマフォ B を取得する場合、コードの他の部分がセマフォ B を取得してからミューテックス A をロックしないようにしてください。
于 2009-12-12T07:15:12.187 に答える
2

あるロックを取得して別のロックを取得しようとすることは避けてください。これにより、循環依存が発生し、デッドロックが発生する可能性があります。それが避けられない場合は、少なくともロックを取得する順序を予測できるようにする必要があります。

RAIIを使用します(例外が発生した場合でもロックが適切に解放されるようにするため)

于 2009-12-12T07:12:50.343 に答える
1

簡単なデッドロックの解決策はありません。

合意された順序でロックを取得する: すべての呼び出しが A->B->C を取得する場合、デッドロックは発生しません。デッドロックは、2 つのスレッド間でロックの順序が異なる場合にのみ発生する可能性があります (1 つは A->B を取得し、2 番目は B->A を取得します)。

実際には、メモリ内の任意のオブジェクト間の順序を選択するのは困難です。単純な些細なプロジェクトでは可能ですが、多くの個々の貢献者がいる大規模なプロジェクトでは非常に困難です。部分的な解決策は、ロックをランク付けして階層を作成することです。モジュール A のすべてのロックにはランク 1 があり、モジュール B のすべてのロックにはランク 2 があります。ランク 1 のロックを保持している場合、ランク 2 のロックを取得できますが、その逆はできません。もちろん、ランキングを追跡して検証するロック プリミティブのフレームワークが必要です。

于 2009-12-12T07:18:54.840 に答える
0

デッドロックの可能性を攻撃したい場合は、デッドロックが存在するための4つの重要な条件の1つを攻撃する必要があります。

デッドロックの4つの条件は次のとおりです。1。相互排除-一度にクリティカルセクションに入ることができるスレッドは1つだけです。2.保留と待機-他のリソースが利用できない場合でも、スレッドは、ジョブを終了しない限り、取得したリソースを解放しません。3.プリエンプションなし-スレッドは他のスレッドよりも優先されません。4.リソースサイクル-他のスレッドからのリソースを待機するスレッドのサイクルチェーンが必要です。

攻撃するのに最も簡単な条件は、サイクルが不可能であることを確認することによるリソースサイクルです。

于 2012-07-11T10:04:42.487 に答える
0

デッドロック: 問題と解決策を読んでください。

「デッドロックを回避するための一般的なアドバイスは、2 つのミューテックスを常に同じ順序でロックすることです。ミューテックス A をミューテックス B の前に常にロックする場合、デッドロックは発生しません。ミューテックスは異なる目的を果たしているため、これは簡単な場合もありますが、ミューテックスがそれぞれ同じクラスの個別のインスタンスを保護している場合など、それほど単純ではない場合もあります。」

于 2009-12-12T07:08:14.360 に答える
0

簡単な「デッドロックの解決策」はたくさんあります。しかし、簡単に適用でき、普遍的に機能するものはありません。

もちろん、最も単純なのは「複数のスレッドを持たない」ことです。

ただし、マルチスレッド アプリケーションを使用していると仮定すると、まだ多くの解決策があります。

共有状態と同期を最小限に抑えることができます。並行して実行され、対話しない 2 つのスレッドがデッドロックすることはありません。デッドロックは、複数のスレッドが同じリソースにアクセスしようとした場合にのみ発生します。なぜ彼らはそうするのですか?それは避けられますか?リソースを再構築または分割して、たとえば 1 つのスレッドがそのリソースに書き込めるようにし、他のスレッドに必要なデータを非同期で渡すことができるか?

おそらく、リソースをコピーして、各スレッドに独自のプライベート コピーを与えて作業できるでしょうか?

そして、他のすべての回答ですでに述べたように、ロックを取得しようとする場合は、グローバルに一貫した順序で取得してください。これを簡単にするために、スレッドが必要とするすべてのロックが 1 回の操作で確実に取得されるようにする必要があります。スレッドがロック A、B、および C を取得する必要がある場合、lock()異なる時間に異なる場所から 3 つの呼び出しを行うべきではありません。混乱し、スレッドが保持しているロックと、まだ取得していないロックを追跡できなくなり、順序を混乱させることになります。必要なすべてのロックを一度に取得できる場合は、それを別の関数呼び出しに分解して、N 個のロックを取得し、デッドロックを回避するために正しい順序で行うことができます。

次に、より野心的なアプローチがあります。CSP のような手法使用すると、スレッド化が非常に単純になり、数千の同時スレッドであっても、正しいことを簡単に証明できます。しかし、これまで慣れ親しんできたものとは大きく異なるプログラムを構築する必要があります。

トランザクショナル メモリはもう 1 つの有望なオプションであり、従来のプログラムに簡単に統合できる可能性があります。しかし、製品品質の実装はまだ非常にまれです。

于 2009-12-12T12:06:33.303 に答える
0

他の人が話した順序を確実にする 1 つの方法は、メモリ アドレスによって定義された順序でロックを取得することです。任意の時点で、シーケンスの早い段階でロックを取得しようとすると、すべてのロックを解放して最初からやり直します。

少しの作業で、システム プリミティブの周りにいくつかのラッパー クラスを使用して、これをほぼ自動的に行うことができます。

于 2009-12-12T07:53:44.473 に答える
0

実用的な治療法はありません。具体的には、コードが同期的に正しいかどうかを単純にテストしたり、緑の V を持つ紳士の規則に従わせたりする方法はありません。

マルチスレッド コードを適切にテストする方法はありません。プログラム ロジックがロック取得のタイミングに依存し、実行ごとに異なる可能性があり、何らかの形で QA の概念が無効になるためです。

私は言うだろう

  • マルチコアマシンのパフォーマンス最適化としてのみスレッドを使用することを好む
  • このパフォーマンスが必要であると確信している場合にのみ、パフォーマンスを最適化します
  • スレッドを使用してプログラム ロジックを単純化できますが、それは自分が何をしているのか完全に確信している場合に限られます。特に注意してください。すべてのロックは非常に小さなコードに限定されています。そのようなコードに初心者を近づけないでください。
  • 航空機の飛行や危険な機械の操作など、ミッション クリティカルなシステムではスレッドを使用しないでください。
  • いずれの場合も、デバッグと QA のコストが高くなるため、スレッドが費用対効果に優れていることはめったにありません。

スレッドを実行するか、既存のコードベースを維持することにした場合:

  • すべてのロックをプリミティブで動作する小さくて単純なコードに限定する
  • 関数呼び出しを避けたり、プログラム フローをロック下で実行されているという事実がすぐには見えない場所に移動したりしないでください。この関数は、将来の作成者によって変更され、制御なしでロック範囲が広がります。
  • オブジェクト内でロックを取得してロック範囲を縮小し、スレッドセーフでないサードパーティ オブジェクトを独自のスレッドセーフ インターフェイスでラップします。
  • ロック状態での実行時に同期通知 (コールバック) を送信しない
  • RAIIロックのみを使用して、例外などのように「他にどうやってここから出ることができるか」を考えるときの認知的負荷を減らします.

マルチスレッドを回避する方法について簡単に説明します。

通常、シングル スレッド設計には、プログラム コンポーネントによって提供されるハートビート関数が含まれ、ループ (ハートビート サイクルと呼ばれます) で呼び出されます。この関数が呼び出されると、すべてのコンポーネントが次の作業を実行し、制御を引き渡す機会が与えられます。また。アルゴリズム担当者がコンポーネント内の「ループ」と考えるのが好きなものは、ステート マシンに変わり、呼び出されたときに次に何をすべきかを識別します。状態は、それぞれのオブジェクトのメンバー データとして維持するのが最適です。

于 2009-12-12T10:10:25.050 に答える