dispatch_sync
做两件事:
- 排队一个块
- 阻塞当前线程,直到该块完成运行
鉴于主线程是一个串行队列(这意味着它只使用一个线程),如果您在主队列上运行以下语句:
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 到当前队列。
如果您想知道读/写上下文数据的函数,有以下三个:
dispatch_queue_set_specific
: 写入队列。
dispatch_queue_get_specific
: 从队列中读取。
dispatch_get_specific
:从当前队列中读取的便利功能。
键通过指针进行比较,并且从不取消引用。setter 中的最后一个参数是释放键的析构函数。
如果您想知道“将一个队列指向另一个队列”,那就是这个意思。例如,我可以将队列 A 指向主队列,它会导致队列 A 中的所有块都在主队列中运行(通常是为了 UI 更新而这样做)。