web-dev-qa-db-ja.com

Grand Central Dispatchでデッドロックを作成するにはどうすればよいですか?

Apple docs、それは言う:

重要:関数に渡す予定の同じキューで実行されているタスクから、dispatch_sync関数またはdispatch_sync_f関数を呼び出さないでください。これは、デッドロックが保証されているシリアルキューでは特に重要ですが、同時キューでは回避する必要があります。

これを正確に行うためのコードをどのように記述しますか?

20
BlackMouse

特定のキューでの意図的なデッドロック:

dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{
        // outer block is waiting for this inner block to complete,
        // inner block won't start before outer block finishes
        // => deadlock
    });

    // this will never be reached
}); 

ここで、外側のブロックと内側のブロックが同じキューで動作していることは明らかです。これが発生するほとんどの場合は、dispatch_syncの呼び出し元が操作しているキューが明確でない場所です。これは通常、元々特定のキューで起動されたクラスでコードを実行している(深く)ネストされたスタックで発生し、誤って同じキューに対してdispatch_syncを呼び出します。

33
Joris Kluivers

デッドロックを作成する単純なコード:

dispatch_queue_t q = dispatch_queue_create("deadlock queue", DISPATCH_QUEUE_SERIAL);

NSLog(@"1");
dispatch_async(q, ^{
    NSLog(@"2");
    dispatch_sync(q, ^{
        NSLog(@"3");
    });
    NSLog(@"4");
});
NSLog(@"5");

ログ出力:

1
5
2

ここで、内部ブロックはシリアルキューqで実行されるようにスケジュールされていますが、現在のブロックが終了するまで実行できません。現在のブロックは、同期的に呼び出されたため、内部で終了するのを待ちます。

14
Vladimir

ブロックする最も簡単な方法はdispatch_sync現在のキュー:

dispatch_sync(dispatch_get_current_queue(), ^{});

これは、現在のキューがメインキューなどのシリアルキューの場合にブロックされます。

8
Nikolai Ruhe

最新のSwift構文:

let queue = DispatchQueue(label: "label")
queue.async {
    queue.sync {
        // outer block is waiting for this inner block to complete,
        // inner block won't start before outer block finishes
        // => deadlock
    }
    // this will never be reached
}

インタビュアーはよく「デッドロックを引き起こす最も簡単な方法は何ですか?」と尋ねます。

Obj-C:

dispatch_sync(dispatch_get_main_queue(), ^{});

迅速:

DispatchQueue.main.sync {}

メインスレッドからsyncを呼び出すと、メインキューがシリアルキューであり、syncは、渡されたブロック/クロージャが完了するまで現在のキューの実行を停止するため、デッドロックが発生します。

1
Bohdan Orlov

誰かが興味を持っている場合、同じキューをターゲットにしてsyncが呼び出されても、並行キューはデッドロックしません。私はそれが明らかであることを知っていますが、シリアルキューだけがそのように動作することを確認する必要がありましたか????

作品:

_let q = DispatchQueue(label: "myQueue", attributes: .concurrent)

q.async {
    print("work async start")
    q.sync {
        print("work sync in async")
    }
    print("work async end")
}

q.sync {
    print("work sync")
}

print("done")
_

失敗:

qlet q = DispatchQueue(label: "myQueue") // implicitly serial queueとして初期化します

1
Alexandru Motoc

Swift 4.2では、次のコードを使用してデッドロックを引き起こす可能性があります。

let aSerialQueue = DispatchQueue(label: "my.label")

aSerialQueue.sync {
    // The code inside this closure will be executed synchronously.
    aSerialQueue.sync {
        // The code inside this closure should also be executed synchronously and on the same queue that is still executing the outer closure ==> It will keep waiting for it to finish ==> it will never be executed ==> Deadlock.
    }
}
0
Mo Abdul-Hameed