如何在不使用 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 @RobNapier dispatch_set_target_queue 是一个异步屏障操作,所以在当前队列中的所有块都执行之前它不会发生。 @RobNapier Session 210 of WWDC 2011 特别声明它是一个异步屏障操作。我在其他地方也读过,只是想不起来在哪里。快速查看源代码可以证实这一点 - 尽管我们都知道我们需要非常小心实现细节。 @BradLarson FWIW,这与 Core Data 采用的方法相同。他们知道performBlockAndWait 有完全相同的问题,而且对他们来说似乎已经足够好了......【参考方案2】:

这是一个非常简单的解决方案。它不如手动使用dispatch_queue_set_specificdispatch_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 函数延迟进程?

如何在不安装的情况下使用数据库?

如何在不使用 Flexbox 的情况下水平对齐元素?

如何在不使用 DESCRIBE 子句的情况下描述 ORACLE 包?

如何在不使用滤镜的情况下使图像变暗? [复制]