デッドロックは見つけるのが難しく、取り除くのが非常に困難です。
コード内のデッドロックのエラー ソースを見つけるにはどうすればよいですか? 「デッドロックパターン」はありますか?
私の特別なケースでは、データベースを扱いますが、この質問はすべてのデッドロックに対して未解決です。
デッドロックは見つけるのが難しく、取り除くのが非常に困難です。
コード内のデッドロックのエラー ソースを見つけるにはどうすればよいですか? 「デッドロックパターン」はありますか?
私の特別なケースでは、データベースを扱いますが、この質問はすべてのデッドロックに対して未解決です。
更新: この最近の MSDN の記事、Tools And Techniques to Identify Concurrency Issuesも興味深いかもしれません
MSDN の記事Deadlock monitorの Stephen Toub は、デッドロックが発生するために必要な次の 4 つの条件を述べています。
限られた数の特定のリソース。C# のモニター (lock キーワードを使用するときに使用するもの) の場合、モニターは相互排他ロックであるため (一度に 1 つのスレッドのみがモニターを所有できることを意味します)、この制限数は 1 です。
1 つのリソースを保持し、別のリソースを要求する機能。C# では、これはあるオブジェクトをロックし、次に別のオブジェクトをロックしてから最初のロックを解放することに似ています。次に例を示します。
lock(a)
{
...
lock(b)
{
...
}
}
プリエンプション機能なし。C# では、これは、あるスレッドが別のスレッドに強制的にロックを解放させることができないことを意味します。
循環待機状態。これは、スレッドのサイクルが存在することを意味します。各スレッドは、続行する前に次のリソースが解放されるのを待機しています。
彼は続けて、デッドロックを回避する方法は、条件 4 を回避 (または阻止) することだと説明しています。
Joe Duffy が 、ロック レベリングとして知られるものを含め、デッドロックを回避および検出するためのいくつかの手法について説明します。ロックの平準化では、ロックに数値が割り当てられ、スレッドは、既に取得したロックよりも大きい数値のロックのみを取得する必要があります。これにより、サイクルの可能性が防止されます。また、今日の典型的なソフトウェア アプリケーションでうまく処理することはしばしば困難であり、ロックを取得するたびにロック レベリングに従わないと、デッドロックが発生します。
古典的なデッドロックのシナリオは、A がロック X を保持していてロック Y を取得しようとしているのに対し、B はロック Y を保持していてロック X を取得しようとしているというものです。使用済み)。
この場合、A と B が同じ順序でロックを取得すると、デッドロックを回避できます。
すべてのトランザクションが同じ順序でテーブルに影響を与えることを確認することが、最も一般的なデッドロックを回避するための鍵です。
例えば:
取引A
UPDATE Table A SET Foo = 'Bar'
UPDATE Table B SET Bar = 'Foo'
取引B
UPDATE Table B SET Bar = 'Foo'
UPDATE Table A SET Foo = 'Bar'
これは、トランザクション Aがテーブル A でロックを取得し、トランザクション Bがテーブル B でロックを取得するため、デッドロックが発生する可能性が非常に高くなります。
他のすべての形式のデッドロックは、通常、高負荷の使用と、リソースが割り当てられている間に SQL Server が内部でデッドロックすることによって発生します。
私の知る限り、デッドロックのパターンはありません (そして 12 年間、高度にマルチスレッド化されたトレーディング アプリケーションを作成してきました)。しかし、TimedLock クラスは、コードに存在するデッドロックを大規模な手直しなしで見つけるのに非常に役立ちました。
http://www.randomtree.org/eric/techblog/archives/2004/10/multithreading_is_hard.html
基本的に、(dotnet/c# で) すべての "lock(xxx)" ステートメントを "using TimedLock.Lock(xxx)" で検索/置換します。
デッドロックが検出された場合 (指定されたタイムアウト内にロックを取得できない、デフォルトは 10 秒)、例外がスローされます。私のローカルバージョンもすぐにスタックトレースを記録します。スタックトレース (できれば行番号付きのデバッグ ビルド) をたどると、失敗した時点で保持されていたロックと、取得しようとしていたロックがすぐにわかります。
dotnet 1.1 では、前述のデッドロックの状況で、運が良ければ、ロックされたすべてのスレッドが同時に例外をスローします。したがって、2 つ以上のスタック トレースと、問題を解決するために必要なすべての情報が得られます。(2.0+ ではスレッド モデルが内部的に変更されているため、これほど幸運ではないかもしれませんが、よくわかりません)
はい - プロセスがランダムな順序でリソースを取得しようとすると、デッドロックが発生します。すべてのプロセスが同じリソースを同じ順序で取得しようとすると、デッドロックの可能性が大幅に減少します。
もちろん、これを手配するのは必ずしも簡単ではありません...
Herb Sutterによるこの記事を読むことをお勧めします。デッドロックの問題の背後にある理由を説明し、この記事でこの問題に取り組むためのフレームワークを提案します。
最も一般的な (私の非科学的な観察によると) DB デッドロックのシナリオは非常に単純です。
これは、読み取りの後に更新を行う場合は、「FOR UPDATE」句 (特定の RDBMS によっては同様のもの) を指定することで回避できます。このようにして、プロセスは最初から排他ロックを取得し、上記のシナリオを不可能にします。
典型的なシナリオは、更新計画の不一致です (テーブルが常に同じ順序で更新されるとは限りません)。ただし、処理量が多い場合にデッドロックが発生することは珍しくありません。
私はデッドロックを現実として受け入れる傾向があります。それはいつか発生するので、デッドロックされた操作を処理して再試行する準備ができている DAL を用意しています。
2 つのプロセスがそれぞれ他のプロセスの完了を待ってから先行する場合に発生する状態。その結果、両方のプロセスがハングします。その最もコモネリーなマルチタスキングとクリント/サーバー。
デッドロックを回避するために、バンカーのアルゴリズムと呼ばれるアルゴリズムがあります。
これは、デッドロックを回避するための役立つ情報も提供します。
デッドロックは、主に複数の依存ロックが存在する場合に発生します。スレッド内で、別のスレッドが逆の順序でミューテックスをロックしようとします。デッドロックを回避するためにミューテックスの使用に注意を払う必要があります。
必ずロックを解除してから操作してください。アクセス順序が ABC のように複数のロックがある場合は、解放順序も ABC にする必要があります。