マルチスレッド アプリケーションを作成する際に発生する最も一般的な問題の 1 つは、デッドロックです。
コミュニティへの私の質問は次のとおりです。
デッドロックとは?
それらをどのように検出しますか?
それらを処理しますか?
最後に、それらの発生をどのように防止しますか?
マルチスレッド アプリケーションを作成する際に発生する最も一般的な問題の 1 つは、デッドロックです。
コミュニティへの私の質問は次のとおりです。
デッドロックとは?
それらをどのように検出しますか?
それらを処理しますか?
最後に、それらの発生をどのように防止しますか?
複数のプロセスが同じリソースに同時にアクセスしようとすると、ロックが発生します。
1 つのプロセスが失われ、他のプロセスが完了するまで待機する必要があります。
デッドロックは、待機中のプロセスが、最初のプロセスが終了する前に必要とする別のリソースをまだ保持している場合に発生します。
したがって、例:
リソース A とリソース B は、プロセス X とプロセス Y によって使用されます
デッドロックを回避する最善の方法は、プロセスがこのように交差しないようにすることです。何かをロックする必要性をできる限り減らします。
データベースでは、1 つのトランザクションでさまざまなテーブルに多くの変更を加えることを避け、トリガーを避け、楽観的/ダーティ/非ロック読み取りにできるだけ切り替えます。
デッドロックは、同時に取得できるロックが 2 つ以上あり、それらが異なる順序で取得された場合にのみ発生します。
デッドロックを回避する方法は次のとおりです。
デッドロックを定義するには、まずプロセスを定義します。
プロセス :私たちが知っているように、プロセスはprogram
実行中です。
リソース :プログラム プロセスを実行するには、いくつかのリソースが必要です。リソース カテゴリには、メモリ、プリンタ、CPU、開いているファイル、テープ ドライブ、CD-ROM などがあります。
デッドロック :デッドロックは、2 つ以上のプロセスがいくつかのリソースを保持しており、さらにリソースを取得しようとしており、実行が完了するまでリソースを解放できない状況または状態です。
デッドロック状態または状況
上の図では、2 つのプロセスP1とp2があり、2 つのリソースR1とR2があります。
リソースR1はプロセスP1に割り当てられ、リソースR2はプロセスp2に割り当てられます。プロセスP1の実行を完了するにはリソースR2が必要であるため、P1はR2を要求しますが、R2は既にP2に割り当てられています。
同様に、プロセスP2が実行を完了するにはR1が必要ですが、R1はすでにP1に割り当てられています。
両方のプロセスは、実行が完了するまでリソースを解放できません。したがって、両方とも別のリソースを待っており、永遠に待機します。したがって、これはデッドロック状態です。
デッドロックが発生するには、4 つの条件が満たされている必要があります。
これらの条件はすべて上図で満たされています。
デッドロックは、決して発生しないものをスレッドが待機しているときに発生します。
通常、スレッドが以前の所有者によって解放されたことのないミューテックスまたはセマフォで待機している場合に発生します。
また、次のような 2 つのスレッドと 2 つのロックが関係する状況でもよく発生します。
Thread 1 Thread 2
Lock1->Lock(); Lock2->Lock();
WaitForLock2(); WaitForLock1(); <-- Oops!
通常、発生すると予想されることが決して起こらないか、アプリケーションが完全にハングするため、それらを検出します。
デッドロックは、OS のマルチプロセッシング/マルチプログラミングの問題でよくある問題です。2 つのプロセス P1、P2 と 2 つのグローバルに共有可能なリソース R1、R2 があり、クリティカル セクションでは両方のリソースにアクセスする必要があるとします。
最初に、OS は R1 をプロセス P1 に割り当て、R2 をプロセス P2 に割り当てます。両方のプロセスが同時に実行されているため、コードの実行を開始できますが、プロセスがクリティカル セクションに到達すると問題が発生します。したがって、プロセス R1 はプロセス P2 が R2 を解放するのを待ち、その逆も同様です...したがって、プロセスは永遠に待ちます (DEADLOCK CONDITION)。
小さな類推...
あなたのお母さん(OS)、
あなた(P1)、
あなたの兄弟(P2)、
リンゴ(R1)、
ナイフ(R2)、
クリティカルセクション(ナイフでリンゴを切る)。あなたのお母さんは最初にリンゴとナイフをあなたの兄弟に渡します。
どちらも幸せで遊んでいます(コードを実行しています)。
リンゴ(クリティカルセクション)を切りたいと思っている人はいるでしょう。
あなたはそのリンゴを弟にあげたくない.
あなたの兄弟はあなたにナイフを渡したくない.
ですから、お二人とも非常に長い間お待ちいただくことになります :)
セクションDeadlockの下で、この素晴らしい記事を見ることができます。これは C# ですが、他のプラットフォームでも考え方は同じです。読みやすいようにここに引用します
デッドロックは、2 つのスレッドがそれぞれ別のスレッドによって保持されているリソースを待機しているときに発生し、どちらも処理を進めることができません。これを説明する最も簡単な方法は、2 つのロックを使用することです。
object locker1 = new object();
object locker2 = new object();
new Thread (() => {
lock (locker1)
{
Thread.Sleep (1000);
lock (locker2); // Deadlock
}
}).Start();
lock (locker2)
{
Thread.Sleep (1000);
lock (locker1); // Deadlock
}
デッドロックは、それぞれがロックされたリソースを保持し、チェーン内の次の要素が保持するリソースをロックしようとするスレッドまたはプロセスの循環チェーンがある場合に発生します。たとえば、それぞれロック A とロック B を保持している 2 つのスレッドが、もう一方のロックを取得しようとしているとします。
デッドロックは、2 つのスレッドがどちらかの進行を妨げるロックを取得すると発生します。それらを回避する最善の方法は、慎重に開発することです。多くの組み込みシステムは、ウォッチドッグ タイマー (システムが一定時間ハングした場合にシステムをリセットするタイマー) を使用して、それらから保護します。
デッドロックは、単一のプロセス/スレッドがアクションを実行できないシステムの状態です。他の人が述べているように、デッドロックは通常、各プロセス/スレッドが別の(または同じ)プロセス/スレッドによってすでにロックされているリソースへのロックを取得しようとする状況の結果です。
それらを見つけて回避するためのさまざまな方法があります。1つは、非常に懸命に考えたり、多くのことを試みたりすることです。ただし、並列処理の処理は非常に困難であり、ほとんどの(すべてではないにしても)人々は問題を完全に回避することはできません。
この種の問題に真剣に取り組む場合は、より正式な方法が役立つことがあります。私が知っている最も実用的な方法は、プロセス理論的アプローチを使用することです。ここでは、システムをいくつかのプロセス言語(CCS、CSP、ACP、mCRL2、LOTOSなど)でモデル化し、利用可能なツールを使用してデッドロック(およびおそらく他のいくつかのプロパティ)を(モデル)チェックします。使用するツールセットの例は、FDR、mCRL2、CADP、Uppaalです。一部の勇敢な魂は、純粋に象徴的な方法を使用して、システムがデッドロックしないことを証明することさえできます(定理証明; Owicki-Griesを探してください)。
ただし、これらの形式手法は通常、ある程度の努力を必要とします(たとえば、プロセス理論の基礎を学ぶ)。しかし、それは単にこれらの問題が難しいという事実の結果だと思います。
デッドロックは、別のプロセスによって要求されたために使用可能なリソースの数が少ない場合に発生する状況です。これは、使用可能なリソースの数がユーザーによって要求された数よりも少なくなると、その時点でプロセスが待機状態になることを意味します。場合によっては待機がさらに増加し、リソース不足の問題をチェックアウトする機会がなくなります。この状況はデッドロックと呼ばれます。実際、デッドロックは私たちにとって大きな問題であり、マルチタスク オペレーティング システムでのみ発生します。シングル タスク オペレーティング システムではデッドロックは発生しません。これは、すべてのリソースが現在実行中のタスクに対してのみ存在するためです......
デッドロックはロックだけで発生するわけではありませんが、それが最も頻繁な原因です。C++ では、各スレッドが他のスレッドの std::thread オブジェクトで join() を呼び出すだけで、ロックなしで 2 つのスレッドでデッドロックを作成できます。
Mutex は本質的にロックであり、共有リソースへの保護されたアクセスを提供します。Linux では、スレッドミューテックスのデータ型は pthread_mutex_t です。使用前に初期化してください。
共有リソースにアクセスするには、ミューテックスをロックする必要があります。ミューテックスが既にロックされている場合、呼び出しはミューテックスがロック解除されるまでスレッドをブロックします。共有リソースへのアクセスが完了したら、ロックを解除する必要があります。
全体として、書かれていない基本原則がいくつかあります。
共有リソースを使用する前にロックを取得してください。
ロックを保持する時間をできるだけ短くします。
スレッドがエラーを返した場合は、ロックを解放します。