dispatch_sync
次の 2 つのことを行います。
- ブロックをキューに入れる
- ブロックの実行が完了するまで現在のスレッドをブロックします
メイン スレッドがシリアル キューである (つまり、1 つのスレッドのみを使用する) 場合、メイン キューで次のステートメントを実行すると、次のようになります。
dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/});
次のイベントが発生します。
dispatch_sync
ブロックをメイン キューに入れます。
dispatch_sync
ブロックの実行が終了するまで、メイン キューのスレッドをブロックします。
dispatch_sync
ブロックが実行されるはずのスレッドがブロックされているため、永久に待機します。
この問題を理解するための鍵は、dispatch_sync
ブロックを実行するのではなく、ブロックをキューに入れるだけであるということです。実行ループの将来の反復で実行が行われます。
次のアプローチ:
if (queueA == dispatch_get_current_queue()){
block();
} else {
dispatch_sync(queueA, block);
}
まったく問題ありませんが、キューの階層が関係する複雑なシナリオからは保護されないことに注意してください。このような場合、現在のキューは、ブロックを送信しようとして以前にブロックされたキューとは異なる場合があります。例:
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
// dispatch_get_current_queue() is B, but A is blocked,
// so a dispatch_sync(A,b) will deadlock.
dispatch_sync(queueA, ^{
// some task
});
});
});
複雑なケースでは、ディスパッチ キューのキー値データを読み書きします。
dispatch_queue_t workerQ = dispatch_queue_create("com.meh.sometask", NULL);
dispatch_queue_t funnelQ = dispatch_queue_create("com.meh.funnel", NULL);
dispatch_set_target_queue(workerQ,funnelQ);
static int kKey;
// saves string "funnel" in funnelQ
CFStringRef tag = CFSTR("funnel");
dispatch_queue_set_specific(funnelQ,
&kKey,
(void*)tag,
(dispatch_function_t)CFRelease);
dispatch_sync(workerQ, ^{
// is funnelQ in the hierarchy of workerQ?
CFStringRef tag = dispatch_get_specific(&kKey);
if (tag){
dispatch_sync(funnelQ, ^{
// some task
});
} else {
// some task
}
});
説明:
workerQ
キューを指すキューを作成しfunnelQ
ます。実際のコードでは、複数の「ワーカー」キューがあり、すべてを一度に再開/一時停止したい場合に便利です (これは、ターゲットfunnelQ
キューを再開/更新することによって実現されます)。
- いつでもワーカー キューをファネルする可能性があるため、ファネルされているかどうかを確認するために、
funnelQ
「ファネル」という単語でタグ付けします。
- 途中で
dispatch_sync
何かをしworkerQ
たり、何らかの理由でしたいのですが、現在のキューdispatch_sync
へfunnelQ
のdispatch_syncを避けているので、タグをチェックしてそれに応じて行動します。get は階層を上に移動するため、値は では見つかりませんが、 でworkerQ
見つかりfunnelQ
ます。これは、階層内のいずれかのキューが値を格納したキューであるかどうかを調べる方法です。したがって、現在のキューへの dispatch_sync を防ぐためです。
コンテキスト データを読み書きする関数について疑問がある場合は、次の 3 つがあります。
dispatch_queue_set_specific
: キューに書き込みます。
dispatch_queue_get_specific
: キューから読み取ります。
dispatch_get_specific
: 現在のキューから読み取る便利な関数。
キーはポインターによって比較され、逆参照されることはありません。セッターの最後のパラメーターは、キーを解放するデストラクタです。
「あるキューを別のキューに向ける」ことについて疑問に思っているなら、それはまさにそれを意味します。たとえば、キュー A をメイン キューに向けると、キュー A 内のすべてのブロックがメイン キューで実行されます (通常、これは UI の更新のために行われます)。