デッドロックとは何ですか?


159

マルチスレッドアプリケーションを作成するときに発生する最も一般的な問題の1つは、デッドロックです。

コミュニティへの私の質問は次のとおりです。

  1. デッドロックとは何ですか?

  2. それらをどのように検出しますか?

  3. それらを処理しますか?

  4. そして最後に、どのようにしてそれらが発生するのを防ぎますか?


回答:


206

ロックは、複数のプロセスが同時に同じリソースにアクセスしようとしたときに発生します。

1つのプロセスは敗北し、もう1つのプロセスが完了するのを待つ必要があります。

デッドロックは、それが終了することができます前に、まず必要であることを待っているプロセスがまだ別のリソースへの保持している場合に発生します。

したがって、例:

リソースAとリソースBはプロセスXとプロセスYによって使用されます

  • XはAの使用を開始します。
  • XとYはBの使用を開始しようとします
  • Yが「勝ち」、最初にBを獲得
  • 今YはAを使用する必要があります
  • AはYを待っているXによってロックされています

デッドロックを回避する最良の方法は、プロセスがこのように交差するのを回避することです。できるだけロックする必要性を減らします。

データベースでは、1つのトランザクションでさまざまなテーブルに多くの変更を加えることを避け、トリガーを避けて、できるだけoptimistic / dirty / nolock読み取りに切り替えます。


9
ここでは、具体的にOSプロセスではなく、一般化としてプロセスを使用しています。これらはスレッドの場合もありますが、まったく異なるアプリケーションやデータベース接続の場合もあります。パターンは同じです。
キース

1
こんにちは、このシナリオを考えると、スレッドAはリソースAをロックし、長いプロセスを持っています。スレッドBがリソースAのロックを待機しています。CPU時間の使用率:20%、デッドロック状態と見なすことができますか?
rickyProgrammer 2017

2
@rickyProgrammerいいえ、それは単なる通常のロック待機ですが、違いは少し学術的なものです。遅いAで待機しているBはロック、Bを待機しているAを待機しているBはデッドロックです。
キース

デッドロックが..リリースされるより、それらのリソースを待っているロックされたリソースを持つ2つのプロセスであるので
rickyProgrammer

2
@rickyProgrammerこれは、循環キューのために、どれほど長く待機してもフリーにならないロックです。
キース

126

犯罪映画からのデッドロック状況の実際の(実際ではない)例を説明しましょう。犯罪者が人質を保持していることを想像してみてください。反対に、警官も犯罪者の友人である人質を保持しています。この場合、警官が彼の友人を手放すことができなければ、犯罪者は人質を手放すつもりはありません。また、警官は犯罪者が人質を解放しない限り、犯罪者の友人を手放すつもりはありません。双方がお互いに最初のステップを主張しているため、これは無限の信頼できない状況です。

犯罪と警官のシーン

ここに画像の説明を入力してください

つまり、2つのスレッドが2つの異なるリソースを必要とし、それぞれに他のスレッドが必要とするリソースのロックがある場合、それはデッドロックです。

デッドロックのもう1つの高レベルの説明:Broken Hearts

あなたは女の子と付き合っており、議論の1日後、双方はお互いに心を砕かれ、I-am-sorry-and-i-missed-youの電話を待っています。この状況では、一方が他方からI-am-sorryコール受信した場合にのみ、両側が相互に通信する必要があります。どちらも通信を開始せず、パッシブ状態で待機しないため、どちらも相手が通信を開始するまで待機し、最終的にデッドロック状態になります。


スレッドが異なるプロセスに属しているのではないですか?同じプロセスに属しているスレッドもデッドロックを引き起こす可能性がありますか?
lordvcs 2017

1
@diabolicfreakスレッドが同じプロセスに属しているかどうかは関係ありません。
Sam Malayek

2
実生活の別の例として、4台の車が2つの等しい道路を同時に4方向に横断する場合があります。誰もが右側から車に道を譲る必要があるので、誰も進むことができません。
LoBo

35

デッドロックは、同時に取得できるロックが2つ以上あり、それらが異なる順序で取得されている場合にのみ発生します。

デッドロックを回避する方法は次のとおりです。

  • ロックを避ける(可能な場合)
  • 複数のロックを避ける
  • 常に同じ順序でロックを取得します。

デッドロックを防ぐための3番目のポイント(常に同じ順序でロックを取得する)は非常に重要です。これは、コーディングの練習で忘れがちです。
Qiang Xu

20

デッドロックを定義するには、最初にプロセスを定義します。

プロセス 私たちが知っているように、プロセスはprogram実行中です。

リソース プログラムプロセスを実行するには、いくつかのリソースが必要です。リソースカテゴリには、メモリ、プリンタ、CPU、オープンファイル、テープドライブ、CD-ROMなどが含まれます。

デッドロック デッドロックは、2つ以上のプロセスがいくつかのリソースを保持し、さらにいくつかのリソースを取得しようとする状況または状態であり、実行が完了するまでリソースを解放できません。

デッドロック状態または状況

ここに画像の説明を入力してください

上の図では、2つのプロセスP1p2があり、2つのリソースR1R2があります。

リソースR1はプロセスP1に割り当てられ、リソースR2はプロセスp2に割り当てられます。プロセスP1の実行を完了するにはリソースR2が必要であるため、P1R2を要求しますが、R2はすでにP2に割り当てられています。

同様に、プロセスP2の実行を完了するにはR1が必要ですが、R1はすでにP1に割り当てられています。

どちらのプロセスも、実行が完了するまで、および実行を完了するまでリソースを解放できません。したがって、どちらも別のリソースを待っており、永久に待機します。したがって、これはデッドロック状態です。

デッドロックが発生するためには、4つの条件が真でなければなりません。

  1. 相互排除 -各リソースは現在、厳密に1つのプロセスに割り当てられているか、使用可能です。(2つのプロセスが同じリソースを同時に制御したり、クリティカルセクションにいることはできません)。
  2. 保留および待機 -現在リソースを保持しているプロセスは、新しいリソースを要求できます。
  3. プリエンプションなし -プロセスがリソースを保持すると、別のプロセスまたはカーネルがリソースを奪うことはできません。
  4. 循環待機 -各プロセスは、別のプロセスによって保持されているリソースを取得するために待機しています。

上記の図では、これらすべての条件が満たされています。


8

デッドロックは、スレッドが発生しないことを待っているときに発生します。

通常、スレッドが前の所有者によって解放されなかったミューテックスまたはセマフォを待機しているときに発生します。

また、次のような2つのスレッドと2つのロックが関係する状況でも頻繁に発生します。

Thread 1               Thread 2

Lock1->Lock();         Lock2->Lock();
WaitForLock2();        WaitForLock1();   <-- Oops!

発生すると予想されることが実行されないか、アプリケーションが完全にハングするため、通常はそれらを検出します。


デッドロックは、スレッドが発生できない何かを待っているときに発生します。
ローン侯爵

4

セクション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
}

4

デッドロックは、OSのマルチプロセッシング/マルチプログラミングの問題における一般的な問題です。2つのプロセスP1、P2と2つのグローバルに共有可能なリソースR1、R2があり、クリティカルセクションで両方のリソースにアクセスする必要があるとします。

最初に、OSはR1をプロセスP1に割り当て、R2をプロセスP2に割り当てます。両方のプロセスが同時に実行されているため、コードの実行を開始できますが、プロセスがクリティカルセクションに到達すると問題が発生します。したがって、プロセスR1はプロセスP2がR2を解放するのを待機し、その逆も同様です。したがって、プロセスR1は永久に待機します(DEADLOCK CONDITION)。

小さなアナロジー...

あなたの母親(OS)、
あなた(P1)、
あなたの兄弟(P2)、
アップル(R1)、
ナイフ(R2)、
クリティカルセクション(ナイフでリンゴを切る)。

あなたのお母さんは初めにあなたに兄弟にリンゴとナイフを渡します。
両方とも幸せで遊んでいます(コードを実行しています)。
誰かが、ある時点でリンゴ(クリティカルセクション)をカットしたいと考えています。
あなたはあなたの兄弟にリンゴを与えたくありません。
あなたの兄弟はあなたにナイフを与えたくないのです。
ですので、あなた方二人は非常に長い間待つことになります:)


2

デッドロックは、2つのスレッドがロックを取得して、どちらかが進行しないようにするときに発生します。それらを回避する最善の方法は、慎重に開発することです。多くの組み込みシステムは、ウォッチドッグタイマー(一定の時間ハングした場合にシステムをリセットするタイマー)を使用して、システムを保護します。


2

デッドロックは、ロックされたリソースを保持するスレッドまたはプロセスの循環チェーンがあり、チェーン内の次の要素が保持するリソースをロックしようとすると発生します。たとえば、それぞれロックAとロックBを保持し、両方が他のロックを取得しようとしている2つのスレッド。


私はあなたに投票します。プロセスまたはスレッドによって混乱したデッドロックが発生するため、あなたの答えは上記よりも簡潔です。誰かがプロセスを言う、誰かがスレッドを言う:)
hainguyen

1

デッドロック状況を理解するための古典的で非常にシンプルなプログラム:-

public class Lazy {

    private static boolean initialized = false;

    static {
        Thread t = new Thread(new Runnable() {
            public void run() {
                initialized = true;
            }
        });

        t.start();

        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println(initialized);
    }
}

メインスレッドはLazy.mainを呼び出すときに、クラスLazyが初期化されているかどうかを確認し、クラスの初期化を開始します。メインスレッドは、初期化をfalseに設定し、runメソッドが初期化をtrueに設定したバックグラウンドスレッドを作成して開始し、バックグラウンドスレッドが完了するまで待機します。

今回は、クラスは現在別のスレッドによって初期化されています。このような状況では、バックグラウンドスレッドである現在のスレッドは、初期化が完了するまでClassオブジェクトを待機します。残念ながら、初期化を行っているスレッド、メインスレッドは、バックグラウンドスレッドが完了するのを待っています。2つのスレッドが相互に待機しているため、プログラムはデッドロックされています。


0

デッドロックは、単一のプロセス/スレッドがアクションを実行することができないシステムの状態です。他の人が述べたように、デッドロックは通常、各プロセス/スレッドが別の(または同じ)プロセス/スレッドによってすでにロックされているリソースへのロックを取得したい状況の結果です。

それらを見つけて回避するには、さまざまな方法があります。1つは、非常に熱心に考えたり、多くのことを試みたりすることです。ただし、並列処理は非常に困難であり、ほとんど(すべてではないにしても)の人々は問題を完全に回避することはできません。

これらの種類の問題への対処を真剣に考えている場合は、より正式な方法がいくつか役立ちます。私が知っている最も実用的な方法は、プロセス理論的アプローチを使用することです。ここでは、システムをいくつかのプロセス言語(CCS、CSP、ACP、mCRL2、LOTOSなど)でモデル化し、利用可能なツールを使用してデッドロック(およびおそらく他のいくつかのプロパティ)を(モデル化)チェックします。使用するツールセットの例は、FDR、mCRL2、CADP、およびUppaalです。一部の勇敢な魂は、純粋にシンボリックな方法を使用して、システムがデッドロックなしであることを証明することさえできます(定理を証明する; Owicki-Griesを探す)。

ただし、これらの正式な方法では、通常、ある程度の努力が必要です(たとえば、プロセス理論の基礎を学ぶ)。しかし、それは単にこれらの問題が難しいという事実の結果だと思います。


0

デッドロックは、別のプロセスから要求されたときに使用可能なリソースの数が少ない場合に発生します。つまり、利用可能なリソースの数がユーザーの要求数より少なくなると、その時点でプロセスは待機状態になります。場合によっては待機がさらに増加し​​、リソース不足の問題を確認する機会がありません。この状況はデッドロックと呼ばれます。実際、デッドロックは私たちにとって大きな問題であり、マルチタスクオペレーティングシステムでのみ発生します。すべてのリソースは現在実行中のタスクにのみ存在するため、シングルタスクオペレーティングシステムではデッドロックは発生しません。


0

上記のいくつかの説明はいいです。これも役立つことを願っていますhttps : //ora-data.blogspot.in/2017/04/deadlock-in-oracle.html

データベースで、セッション(oraなど)が別のセッション(データなど)によって保持されるリソースを必要とするが、そのセッション(データ)も最初のセッション(ora)によって保持されるリソースを必要とする場合。2つ以上のセッションが含まれる場合もありますが、考え方は同じです。実際、デッドロックは一部のトランザクションが機能し続けるのを妨げます。次に例を示します。ORA-DATAがロックAを保持してロックBを要求し、SKUがロックBを保持してロックAを要求するとします。

おかげで、


0

デッドロックは、スレッドが他のスレッドの終了を待っているときに発生します。

回避する方法は?
-ネストされたロックを
回避する-不要なロックを回避
する-スレッドjoin()を使用する

どのように検出しますか?
このコマンドをcmdで実行します。

jcmd $PID Thread.print

リファレンス:geeksforgeeks


0

デッドロックはロックで発生するだけではありませんが、それが最もよくある原因です。C ++では、各スレッドにstd :: threadオブジェクトのjoin()を呼び出すだけで、2つのスレッドでロックなしのデッドロックを作成できます。


0

ロックベースの同時実行制御

共有リソースへのアクセスを制御するためにロックを使用すると、デッドロックが発生する傾向があり、トランザクションスケジューラだけではその発生を防ぐことはできません。

たとえば、リレーショナルデータベースシステムはさまざまなロックを使用して、トランザクションのACIDプロパティを保証します

使用しているリレーショナルデータベースシステムに関係なく、変更時には常にロックが取得されます(たとえば、 UPDATEDELETE、特定のテーブルレコードをまたはなど)ます。現在実行中のトランザクションによって変更された行をロックしないと、原子性が損なわれます。

デッドロックとは

私はで説明したように、この記事、デッドロックは、2つの同時トランザクションが以下の図に示すように、ロックを解除するために他のための各待ちため進捗状況を作ることができないときに起こります。

ここに画像の説明を入力してください

どちらのトランザクションもロック取得フェーズにあるため、どちらも次のトランザクションを取得する前にロックを解放します。

デッドロック状態からの回復

ロックに依存する同時実行制御アルゴリズムを使用している場合は、常にデッドロック状態で実行されるリスクがあります。デッドロックは、データベースシステムだけでなく、あらゆる同時実行環境で発生する可能性があります。

たとえば、マルチスレッドプログラムは、2つ以上のスレッドが以前に取得されたロックを待機していて、どのスレッドも進行できない場合、デッドロックする可能性があります。これがJavaアプリケーションで発生した場合、JVMはスレッドにその実行を停止させ、そのロックを解放させることはできません。

場合であってもThread、クラスが公開stopメソッドを、スレッドが停止した後、オブジェクトが不整合な状態のままになる可能性があるため、Java 1.1以降、そのメソッドは非推奨になりました。代わりに、Javaはinterruptメソッドを定義します。これは、割り込みを受けたスレッドが割り込みを無視してその実行を継続できるため、ヒントとして機能します。

このため、Javaアプリケーションはデッドロック状態から回復することはできません。デッドロックが発生しないようにロック取得要求を順序付けるのはアプリケーション開発者の責任です。

ただし、特定のトランザクションがさらに取得したい他のロックを予測することは不可能であるため、データベースシステムは特定のロック取得順序を強制できません。ロック順序の保持はデータアクセス層の責任となり、データベースはデッドロック状態からの回復のみを支援できます。

データベースエンジンは、現在の競合グラフをスキャンしてロック待機サイクル(デッドロックが原因)を探す別のプロセスを実行します。サイクルが検出されると、データベースエンジンは1つのトランザクションを選択して中止し、そのロックを解放して、他のトランザクションが進行できるようにします。

JVMとは異なり、データベーストランザクションは、アトミックな作業単位として設計されています。したがって、ロールバックによってデータベースは一貫した状態になります。

このトピックの詳細については、こちらの記事もご覧ください。


-2

本質的にミューテックスはロックであり、共有リソースへの保護されたアクセスを提供します。Linuxでは、スレッドミューテックスのデータ型はpthread_mutex_tです。使用する前に初期化してください。

共有リソースにアクセスするには、ミューテックスをロックする必要があります。mutexがすでにロックされている場合、呼び出しはmutexがロック解除されるまでスレッドをブロックします。共有リソースへのアクセスが完了したら、それらのロックを解除する必要があります。

全体として、いくつかの未記述の基本原則があります。

  • 共有リソースを使用する前にロックを取得してください。

  • 可能な限り短い時間でロックを保持する。

  • スレッドがエラーを返した場合、ロックを解放します。


3
これは、デッドロックではなくロックについて記述しています。
ローンの侯爵
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.