如何在不使用 dispatch_get_current_queue() 的情况下验证我是不是在给定的 GCD 队列上运行?
Posted
技术标签:
【中文标题】如何在不使用 dispatch_get_current_queue() 的情况下验证我是不是在给定的 GCD 队列上运行?【英文标题】:How can I verify that I am running on a given GCD queue without using dispatch_get_current_queue()?如何在不使用 dispatch_get_current_queue() 的情况下验证我是否在给定的 GCD 队列上运行? 【发布时间】:2012-09-30 03:59:48 【问题描述】:最近,我需要一个函数来保证在特定串行调度队列上同步执行给定块。这个共享函数可能会从已经在该队列上运行的东西中调用,所以我需要检查这种情况,以防止同步分派到同一个队列时出现死锁。
我使用如下代码来做到这一点:
void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
dispatch_queue_t videoProcessingQueue = [GPUImageOpenGLESContext sharedOpenGLESQueue];
if (dispatch_get_current_queue() == videoProcessingQueue)
block();
else
dispatch_sync(videoProcessingQueue, block);
此函数依赖于使用dispatch_get_current_queue()
来确定运行此函数的队列的身份,并将其与目标队列进行比较。如果匹配,它知道只运行块内联而不调度到该队列,因为函数已经在其上运行。
我听说过使用dispatch_get_current_queue()
进行这样的比较是否合适,我在标题中看到了这样的措辞:
建议仅用于调试和记录目的:
代码不得对返回的队列做出任何假设, 除非它是全局队列之一或代码本身具有的队列 创建。代码不能假定同步执行到 如果该队列不是由返回的队列,则队列不会死锁 dispatch_get_current_queue().
此外,在 ios 6.0 中(但尚不适用于 Mountain Lion),GCD 标头现在将此功能标记为已弃用。
听起来我不应该以这种方式使用此功能,但我不确定应该使用什么来代替它。对于上述针对主队列的函数,我可以使用[NSThread isMainThread]
,但是如何检查我是否在我的自定义串行队列之一上运行,以便防止死锁?
【问题讨论】:
【参考方案1】:使用dispatch_queue_set_specific()
分配您想要的任何标识符。然后,您可以使用 dispatch_get_specific()
检查您的标识符。
请记住,dispatch_get_specific()
很好,因为它会从当前队列开始,然后如果当前队列上没有设置密钥,则沿着目标队列前进。这通常无关紧要,但在某些情况下可能很有用。
【讨论】:
是的,这看起来确实是可行的方法,并且得到了 Apple 开发者论坛上有趣的讨论的支持:devforums.apple.com/message/710745#714753 如果您对要保护的队列使用唯一键(而不是唯一值),“遍历目标队列”实际上非常有用。这样,如果您当前在针对您的视频队列的队列中,您就可以更加确定不会死锁。您不能绝对确定,因为队列可以随时更改其目标,因此您可以将一个块排入队列,然后在块执行之前让其他一些块将您的队列重新定位到视频队列。所以,嗯……不要那样做。 :D @RobNapierdispatch_set_target_queue
是一个异步屏障操作,所以在当前队列中的所有块都执行之前它不会发生。
@RobNapier Session 210 of WWDC 2011 特别声明它是一个异步屏障操作。我在其他地方也读过,只是想不起来在哪里。快速查看源代码可以证实这一点 - 尽管我们都知道我们需要非常小心实现细节。
@BradLarson FWIW,这与 Core Data 采用的方法相同。他们知道performBlockAndWait
有完全相同的问题,而且对他们来说似乎已经足够好了......【参考方案2】:
这是一个非常简单的解决方案。它不如手动使用dispatch_queue_set_specific
和dispatch_get_specific
那样高效——我没有这方面的指标。
#import <libkern/OSAtomic.h>
BOOL dispatch_is_on_queue(dispatch_queue_t queue)
int key;
static int32_t incrementer;
CFNumberRef value = CFBridgingRetain(@(OSAtomicIncrement32(&incrementer)));
dispatch_queue_set_specific(queue, &key, value, nil);
BOOL result = dispatch_get_specific(&key) == value;
dispatch_queue_set_specific(queue, &key, nil, nil);
CFRelease(value);
return result;
【讨论】:
以上是关于如何在不使用 dispatch_get_current_queue() 的情况下验证我是不是在给定的 GCD 队列上运行?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不更改链接结构的情况下使用 \ 转义字符 (、)、[、]、*、_、:[]()
如何在不使用 sleep() 的情况下使用 ontimer 函数延迟进程?