web-dev-qa-db-ja.com

デッドロックとライブロックの違いは何ですか?

誰かが(コードの)例を使ってデッドロックlivelockの違いを教えてください。

299
macindows

http://ja.wikipedia.org/wiki/Deadlock より

並行コンピューティングでは、デッドロックは、アクションのグループの各メンバーが、他のメンバーがロックを解放するのを待っている状態です。

Alivelockはデッドロックに似ていますが、livelockに関連するプロセスの状態が常に互いに変化し、進行していないことが異なります。 Livelockはリソース不足の特別な場合です。一般的な定義は、特定のプロセスが進行していないことを示しているだけです。

実際のライブロックの例は、狭い廊下で2人が出会い、それぞれが別の道を通すために横に移動することによって丁寧になろうとしますが、彼らは両方とも繰り返し移動するため、何もせずに左右に揺れてしまいます同時に同じ方法で。

ライブロックは、デッドロックを検出してデッドロックから回復するアルゴリズムによっては危険です。複数のプロセスが処理を実行すると、デッドロック検出アルゴリズムが繰り返し起動される可能性があります。これは、(ランダムにまたは優先順位によって選択された)1つのプロセスのみが確実にアクションを実行するようにすることで回避できます。

350
mah

Livelock

スレッドは他のスレッドの動作に応じて動作することがよくあります。他のスレッドのアクションが他のスレッドのアクションに対する応答でもある場合、livelockが発生する可能性があります。

デッドロックと同様に、ライブロックされたスレッドはさらに進歩することはできません。しかしながら、スレッドはブロックされません - それらは仕事を再開するためにお互いに応答するだけで忙しすぎます。これは、2人が廊下でお互いを通り過ぎようとしているのと同じです。AlphonseはGastonを通過させるために左に移動し、GastonはAlphonseを通過させるために右に移動します。彼らがまだお互いをブロックしているのを見て、ガストンが彼の左に動く間、アルフォンスは彼の右に動く。彼らはまだお互いをブロックしています。

livelockdeadlockの主な違いは、スレッドがブロックされないことです。代わりに、彼らは継続的にお互いに応答しようとします。

この画像では、両方の円(スレッドまたはプロセス)が左右に移動することによってもう一方にスペースを与えようとします。しかし、彼らはそれ以上動くことはできません。

enter image description here

71
Burusothman

ここにあるすべての内容と例は、

オペレーティングシステム:内部と設計原則
ウィリアム・ストーリングス
8ºエディション

Deadlock:それぞれが他の人が何かをするのを待っているために2つ以上のプロセスが進行できない状況。

たとえば、P1とP2の2つのプロセスと、R1とR2の2つのリソースを考えてみましょう。各プロセスがその機能の一部を実行するために両方のリソースにアクセスする必要があるとします。その場合、OSはP2にR1を割り当て、P1にR2を割り当てます。各プロセスは2つのリソースのうちの1つを待っています。他のリソースを獲得し、両方のリソースを必要とする機能を実行するまでは、既に所有しているリソースも解放されません。 2つのプロセスがデッドロックしています

Livelock:2つ以上のプロセスが、他のプロセスの変化に応じて、有用な作業をせずに状態を継続的に変更する状況。

飢餓:実行可能なプロセスがスケジューラによって無期限に見落とされる状況。続行することはできますが、選択されることはありません。

3つのプロセス(P1、P2、P3)がそれぞれリソースRに定期的にアクセスする必要があるとします。P1がリソースを所有し、P2とP3の両方が遅延してそのリソースを待機する状況を考えます。 P1がそのクリティカルセクションを出るとき、P2またはP3のいずれかがRへのアクセスを許可されるべきである。OSがP3へのアクセスを許可し、P3がそのクリティカルセクションを完了する前にP1が再びアクセスを要求すると仮定する。 P3が終了した後にOSがP1へのアクセスを許可し、続いてP1とP3へのアクセスを交互に許可すると、デッドロックの状況がなくても、P2はリソースへのアクセスを無期限に拒否される可能性があります。

付録A - 同僚のトピック

デッドロックの例

どちらかがwhile文を実行する前に両方のプロセスがフラグをtrueに設定すると、それぞれがもう一方がクリティカルセクションに入ったと判断し、デッドロックを引き起こします。

/* PROCESS 0 */
flag[0] = true; 
while (flag[1]) 
    /* do nothing */; 
/* critical section*/; 
flag[0] = false; 

 /* PROCESS 1 */
flag[1] = true;
while (flag[0])
    /* do nothing */;
/* critical section*/;
flag[1] = false;

ライブロックの例

/* PROCESS 0 */
flag[0] = true; 
while (flag[1]){
    flag[0] = false; 
    /*delay */;
    flag[0] = true;
}
/*critical section*/;
flag[0] = false; 

/* PROCESS 1 */
flag[1] = true;
while (flag[0]) {
    flag[1] = false;
    /*delay */;
    flag[1] = true;
}
/* critical section*/;
flag[1] = false;

[...]次の一連のイベントを検討してください。

  • P0はフラグ[0]をtrueに設定します。
  • P1はフラグ[1]をtrueに設定します。
  • P0はフラグ[1]をチェックします。
  • P1はフラグ[0]をチェックします。
  • P0はフラグ[0]をfalseに設定します。
  • P1はフラグ[1]をfalseに設定します。
  • P0はフラグ[0]をtrueに設定します。
  • P1はフラグ[1]をtrueに設定します。

このシーケンスは無期限に拡張することができ、どちらのプロセスもその重要なセクションに入ることはできませんでした。厳密に言えば、これはデッドロックではありませんです。2つのプロセスの相対速度を変更すると、このサイクルが中断され、重要なセクションに入ることができるためです。この状態はlivelockと呼ばれます。一連のプロセスがそれらの重要なセクションに入りたいがプロセスが成功できない場合にデッドロックが発生することを思い出してください。 livelockを使用すると、成功する可能性のある実行シーケンスがありますが、プロセスがクリティカルセクションに入らない1つ以上の実行シーケンスを記述することもできます。

DEADLOCKデッドロックは、決して満たされない条件をタスクが無期限に待つ条件です。 - タスクは共有リソースに対する排他制御を要求します - タスクは他のリソースの解放を待つ間リソースを保持します - タスクはできませんリソースを強制的に再言語化する - 循環的な待機状態が存在する

LIVELOCK複数のタスクが複数のリソースに依存して使用すると、それらのタスクが永久に実行され続けるという循環依存状態が発生し、優先順位の低いすべてのタスクが実行されなくなります。優先タスクは飢餓と呼ばれる状態を経験します)

13

これら2つの例は、デッドロックとライブロックの違いを説明しているのかもしれません。


デッドロックのJavaの例:

import Java.util.concurrent.locks.Lock;
import Java.util.concurrent.locks.ReentrantLock;

public class DeadlockSample {

    private static final Lock lock1 = new ReentrantLock(true);
    private static final Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Thread threadA = new Thread(DeadlockSample::doA,"Thread A");
        Thread threadB = new Thread(DeadlockSample::doB,"Thread B");
        threadA.start();
        threadB.start();
    }

    public static void doA() {
        System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
        lock1.lock();
        System.out.println(Thread.currentThread().getName() + " : holds lock 1");

        try {
            System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
            lock2.lock();
            System.out.println(Thread.currentThread().getName() + " : holds lock 2");

            try {
                System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
            } finally {
                lock2.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
            }
        } finally {
            lock1.unlock();
            System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
        }
    }

    public static void doB() {
        System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
        lock2.lock();
        System.out.println(Thread.currentThread().getName() + " : holds lock 2");

        try {
            System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
            lock1.lock();
            System.out.println(Thread.currentThread().getName() + " : holds lock 1");

            try {
                System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
            } finally {
                lock1.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
            }
        } finally {
            lock2.unlock();
            System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
        }
    }
}

出力例:

Thread A : waits for lock 1
Thread B : waits for lock 2
Thread A : holds lock 1
Thread B : holds lock 2
Thread B : waits for lock 1
Thread A : waits for lock 2

ライブロックのJavaの例:

import Java.util.concurrent.locks.Lock;
import Java.util.concurrent.locks.ReentrantLock;

public class LivelockSample {

    private static final Lock lock1 = new ReentrantLock(true);
    private static final Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Thread threadA = new Thread(LivelockSample::doA, "Thread A");
        Thread threadB = new Thread(LivelockSample::doB, "Thread B");
        threadA.start();
        threadB.start();
    }

    public static void doA() {
        try {
            while (!lock1.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + " : holds lock 1");

            try {
                while (!lock2.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
                    Thread.sleep(100);
                }
                System.out.println(Thread.currentThread().getName() + " : holds lock 2");

                try {
                    System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
                } finally {
                    lock2.unlock();
                    System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
                }
            } finally {
                lock1.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
            }
        } catch (InterruptedException e) {
            // can be ignored here for this sample
        }
    }

    public static void doB() {
        try {
            while (!lock2.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + " : holds lock 2");

            try {
                while (!lock1.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
                    Thread.sleep(100);
                }
                System.out.println(Thread.currentThread().getName() + " : holds lock 1");

                try {
                    System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
                } finally {
                    lock1.unlock();
                    System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
                }
            } finally {
                lock2.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
            }
        } catch (InterruptedException e) {
            // can be ignored here for this sample
        }
    }
}

出力例:

Thread B : holds lock 2
Thread A : holds lock 1
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
...

どちらの例でも、スレッドは異なる順序でロックを取得するように強制されます。デッドロックが他のロックを待機している間、ライブロックは実際には待機しません。ロックを取得する機会がなくても、必然的にロックを取得しようとします。試行するたびにCPUサイクルが消費されます。

5
mmirwaldt