Apple docs、それは言う:
重要:関数に渡す予定の同じキューで実行されているタスクから、dispatch_sync関数またはdispatch_sync_f関数を呼び出さないでください。これは、デッドロックが保証されているシリアルキューでは特に重要ですが、同時キューでは回避する必要があります。
これを正確に行うためのコードをどのように記述しますか?
特定のキューでの意図的なデッドロック:
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
を呼び出します。
デッドロックを作成する単純なコード:
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
で実行されるようにスケジュールされていますが、現在のブロックが終了するまで実行できません。現在のブロックは、同期的に呼び出されたため、内部で終了するのを待ちます。
ブロックする最も簡単な方法はdispatch_sync
現在のキュー:
dispatch_sync(dispatch_get_current_queue(), ^{});
これは、現在のキューがメインキューなどのシリアルキューの場合にブロックされます。
最新の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
は、渡されたブロック/クロージャが完了するまで現在のキューの実行を停止するため、デッドロックが発生します。
誰かが興味を持っている場合、同じキューをターゲットにして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")
_
失敗:
q
をlet q = DispatchQueue(label: "myQueue") // implicitly serial queue
として初期化します
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.
}
}