gpt4 book ai didi

objective-c - 为什么我们不能在当前队列上使用 dispatch_sync?

转载 作者:IT老高 更新时间:2023-10-28 11:30:30 25 4
gpt4 key购买 nike

我遇到了一个场景,我有一个委托(delegate)回调,它可能发生在主线程或另一个线程上,直到运行时我才知道是哪个(使用 StoreKit.framework)。

我还有需要在回调中更新的 UI 代码,这需要在函数执行之前发生,所以我最初的想法是有一个这样的函数:

-(void) someDelegateCallback:(id) sender
{
dispatch_sync(dispatch_get_main_queue(), ^{
// ui update code here
});

// code here that depends upon the UI getting updated
}

当它在后台线程上执行时,效果很好。但是,当在主线程上执行时,程序会陷入死锁。

这对我来说似乎很有趣,如果我正确阅读了 dispatch_sync 的文档,那么我希望它直接执行该 block ,而不用担心将其调度到运行循环中,如 here 所述:

As an optimization, this function invokes the block on the current thread when possible.

但是,这没什么大不了的,它只是意味着更多的打字,这导致我采用这种方法:

-(void) someDelegateCallBack:(id) sender
{
dispatch_block_t onMain = ^{
// update UI code here
};

if (dispatch_get_current_queue() == dispatch_get_main_queue())
onMain();
else
dispatch_sync(dispatch_get_main_queue(), onMain);
}

但是,这似乎有点倒退。这是 GCD 制作中的错误,还是我在文档中遗漏了什么?

最佳答案

dispatch_sync 做了两件事:

  1. 排队一个 block
  2. 阻塞当前线程,直到该 block 完成运行

鉴于主线程是一个串行队列(这意味着它只使用一个线程),如果您在主队列上运行以下语句:

dispatch_sync(dispatch_get_main_queue(), ^(){/*...*/});

将发生以下事件:

  1. dispatch_sync 将 block 放入主队列中。
  2. dispatch_sync 阻塞主队列的线程,直到该 block 完成执行。
  3. dispatch_sync 永远等待,因为应该运行 block 的线程被阻塞了。

理解这个问题的关键是 dispatch_sync 不执行 block ,它只是将它们排队。将在运行循环的 future 迭代中执行。

以下做法:

if (queueA == dispatch_get_current_queue()){
block();
} else {
dispatch_sync(queueA, block);
}

非常好,但请注意,它不会保护您免受涉及队列层次结构的复杂场景的影响。在这种情况下,当前队列可能与您尝试发送 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
}
});

解释:

  • 我创建了一个指向 funnelQ 队列的 workerQ 队列。在实际代码中,如果您有多个“工作”队列并且您想要一次恢复/暂停所有队列(通过恢复/更新它们的目标 funnelQ 队列来实现),这将非常有用。
  • 我可能会在任何时间点汇集我的工作队列,所以要知道它们是否汇集,我用“漏斗”一词标记 funnelQ
  • 在路上我 dispatch_sync 一些东西到 workerQ,无论出于何种原因我想 dispatch_syncfunnelQ ,但避免将 dispatch_sync 发送到当前队列,因此我检查标签并采取相应措施。因为 get 沿着层次结构向上走,所以不会在 workerQ 中找到该值,但会在 funnelQ 中找到该值。这是一种找出层次结构中是否有任何队列是我们存储值的队列的方法。因此,为了防止 dispatch_sync 到当前队列。

如果您想知道读取/写入上下文数据的函数,有以下三个:

  • dispatch_queue_set_specific:写入队列。
  • dispatch_queue_get_specific:从队列中读取。
  • dispatch_get_specific:从当前队列中读取的便捷函数。

键通过指针进行比较,并且从不取消引用。 setter 中的最后一个参数是释放键的析构函数。

如果您想知道“将一个队列指向另一个队列”,那就是这个意思。例如,我可以将队列 A 指向主队列,它会导致队列 A 中的所有 block 都在主队列中运行(通常是为了 UI 更新而这样做)。

关于objective-c - 为什么我们不能在当前队列上使用 dispatch_sync?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10984732/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com