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
queue).
- 我可以在任何时间点集中我的工作队列,因此要知道它们是否集中,我标记
funnelQ
用“漏斗”这个词。
- 沿着路我
dispatch_sync
某事workerQ
,无论出于什么原因我想要dispatch_sync
to funnelQ
,但避免将dispatch_sync发送到当前队列,因此我检查标签并采取相应措施。因为 get 会沿着层次结构向上走,所以不会在中找到该值workerQ
但它会被发现在funnelQ
。这是一种查明层次结构中是否有任何队列是我们存储值的队列的方法。因此,要防止对当前队列进行dispatch_sync。
如果您想知道读/写上下文数据的函数,有以下三个:
-
dispatch_queue_set_specific
:写入队列。
-
dispatch_queue_get_specific
:从队列中读取。
-
dispatch_get_specific
:从当前队列读取的便捷功能。
键通过指针进行比较,并且永远不会取消引用。 setter 中的最后一个参数是释放键的析构函数。
如果您想知道“将一个队列指向另一个队列”,这正是这个意思。例如,我可以将队列A指向主队列,这将导致队列A中的所有块都在主队列中运行(通常这是为了UI更新而完成的)。