我在哪个 GCD 队列上运行,无论是不是是主队列?
Posted
技术标签:
【中文标题】我在哪个 GCD 队列上运行,无论是不是是主队列?【英文标题】:What GCD queue, main or not, am I running on?我在哪个 GCD 队列上运行,无论是否是主队列? 【发布时间】:2011-02-09 08:25:35 【问题描述】:我正在尝试编写一些线程安全的方法,所以我正在使用:
...
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_sync(main,^
[self doSomethingInTheForeground];
);
...
但是如果我在不必要的主线程上,我可以跳过所有那些调度调用,所以我想知道我目前在哪个线程上。我怎么知道?
或者,也许这样做并没有什么不同(在性能上)?
这样比较可以吗?
if (dispatch_get_main_queue() == dispatch_get_current_queue())...
【问题讨论】:
使用提到的一种技术不仅可以减少开销,而且如果您对正在运行的同一队列执行 dispatch_sync,您将死锁。 dispatch_async 没问题。 如果您需要更通用的解决方案来防止 libdispatch 中的死锁,请查看这些帮助程序。他们不覆盖 main_queue,只是要小心。 gist.github.com/1205760 这个问题很好,但是示例代码显示了死锁模式。也许修改一下比较好。 【参考方案1】:更新答案:
Apple 文档已更改,现在说“当从提交块的上下文之外调用时,如果调用是从主线程执行的,则此函数返回主队列。如果调用是从任何其他线程进行的,此函数返回默认的并发队列。”所以检查dispatch_get_main_queue() == dispatch_get_current_queue()
应该可以。
原答案:
使用dispatch_get_main_queue() == dispatch_get_current_queue()
将不起作用。 dispatch_get_current_queue
的文档说“当在提交块的上下文之外调用时,此函数返回默认并发队列”。默认并发队列不是主队列。
[NSThread isMainThread]
应该可以满足您的需求。请注意,[NSThread isMainThread]
可以适用于除主队列以外的队列,例如,当从主线程调用 dispatch_sync
时。
【讨论】:
实际上,它说“当从提交块的上下文之外调用时,此函数如果调用是从主线程执行的,则返回主队列。如果调用是从任何其他线程进行的,此函数返回默认的并发队列。” 非常感谢您的更新。我认为这是我们都期望它的工作方式:) 看起来dispatch_get_current_queue()
现在已被弃用,有什么替代方案?
你应该使用 dispatch_get_specific。请参阅***.com/a/15725847/412916 以及 cmets 中 Apple 论坛的链接。【参考方案2】:
随着dispatch_get_current_queue()
的折旧,相当于(dispatch_get_main_queue() == dispatch_get_current_queue())
现在是通过队列标签比较是:
(dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))
【讨论】:
请注意 dispatch_queue_get_label() 返回一个字符 *。检查两个调用返回的指针值是否足够? strcmp() 调用是多余的吗?【参考方案3】:更新答案:
dispatch_get_current_queue() 现已弃用。
原答案:
在 OS-X 10.8 上,在头文件 (queue.h) 中,在 dispatch_get_current_queue() 函数上方的注释中是这样写的:
当在主线程调用 dispatch_get_current_queue() 时,它可能 或者可能不会返回与 dispatch_get_main_queue() 相同的值。 比较两者不是测试代码是否正在执行的有效方法 在主线程上。
我发现是因为
assert(dispatch_get_current_queue() == dispatch_get_main_queue());
在我在 ios 上运行良好但在 OS-X 上失败的代码中。
【讨论】:
【参考方案4】:iOS 的新功能:10.0+ 和 macOS:10.12+(tvOS:10.0+,watchOS:3.0+)。
斯威夫特 4+:
检查是否在主队列上运行:
if (RunLoop.current == RunLoop.main)
print("On main queue")
断言是否在主队列上运行:
dispatchPrecondition(condition: .onQueue(.main))
// Code running on main queue
断言如果不在主队列上运行:
dispatchPrecondition(condition: .notOnQueue(.main))
// Code NOT running on main queue
ObjC:检查是否在主队列上运行:
if ([[NSRunLoop currentRunLoop] isEqual: [NSRunLoop mainRunLoop]])
NSLog(@"Running on main");
else
NSLog(@"Not running on main");
断言是否在主队列上运行:
dispatch_assert_queue(dispatch_get_main_queue());
断言如果不在主队列上运行:
dispatch_assert_queue_not(dispatch_get_main_queue());
注意:
mainThread != mainQueue
主队列总是在主队列上运行 线程,但一个队列可能正在主线程上运行而没有 主队列。所以,不要混合使用线程和队列测试!
【讨论】:
【参考方案5】:如果你在 Objective-C 中,并且希望在主线程上同步发生一些事情,使用它会不会更简单
[self performSelectorOnMainThread: @selector(doSomethingInTheForeground)
withObject: nil
waitUntilDone: YES];
这样做的好处是,如果你已经在主线程上,没关系,消息以正常方式发送。
【讨论】:
【参考方案6】:使用dispatch_get_current_queue()
是不安全的,除非你没有调试。写得很清楚in the dispatch_queue
man page:
注意事项
代码无法对
dispatch_get_current_queue()
返回的队列做出任何假设。返回的队列可能有任意的策略,这些策略可能会让试图调度队列工作的代码感到惊讶。策略列表包括但不限于队列宽度(即串行与并发)、调度优先级、安全凭证或文件系统配置。因此,dispatch_get_current_queue()
只能用于身份测试或调试。
更好的事情(可能更复杂)是同步后台线程:
dispatch_sync(backgroundqueue,^
[self doSomethingInTheBackground];
);
也许我完全错了,但这就是我的建议。
【讨论】:
明确声明“用于身份测试”没问题。这就是最初的问题所暗示的(尽管这不会像我在回答中提到的那样起作用)。【参考方案7】:其实可以用的。
文档说
当从提交块的上下文之外调用时,这个 如果调用是从主队列执行的,函数返回主队列 线程。如果调用是从任何其他线程进行的,则此函数 返回默认的并发队列。
【讨论】:
【参考方案8】:请注意,主队列与主线程不同。当在主线程的队列上使用 dispatch_sync() 时,非主队列很容易在主线程上运行。更罕见的是,在使用 dispatch_main() 代替 NSRunLoops 的命令行工具中(即 Cocoa/iOS 应用程序除外),主队列可能在主线程以外的其他地方执行。
如果您正在处理需要主队列而不仅仅是主线程的代码(例如,期望从 queue_get_specific 在主队列上设置的值的代码,据传闻 VectorKit/MapKit 会这样做),最好显式检查主队列而不是主线程。
一个显式检查主队列的选项:
BOOL MyIsMainQueue(void)
static char MAIN_IND_KEY;
static char MAIN_IND_VAL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
dispatch_queue_set_specific(dispatch_get_main_queue(), &MAIN_IND_KEY, &MAIN_IND_VAL, NULL);
);
return dispatch_get_specific(&MAIN_IND_KEY) == &MAIN_IND_VAL;
使用 DISPATCH_CURRENT_QUEUE_LABEL 的答案也应该有效,并且可能会更好,尽管在 MacOS 10.9 和 iOS7 之前定义 DISPATCH_CURRENT_QUEUE_LABEL 时可能无效(即崩溃)。
【讨论】:
【参考方案9】:dispatch_get_current_queue
自 iOS 6.0 起已弃用。看起来[NSThread isMainThread]
是唯一的出路。
【讨论】:
我在苹果和第三方的许多教程和文档中看到了 dispatch_get_main_queue 的用法。它似乎是一个非常广泛使用的功能。我因此挑战它一般被弃用。能给个参考吗?有直接替代策略吗? 你是对的,我剪切/粘贴函数的名称是错误的。不推荐使用的是 dispatch_get_current_queue 。很抱歉造成混乱。然而,结论仍然成立。 [NSThread isMainThread] 是唯一的方法(尽管它并不总是意味着你在主队列中)。 啊哈,对我来说也是新的,但是我同意。 :)以上是关于我在哪个 GCD 队列上运行,无论是不是是主队列?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不使用 dispatch_get_current_queue() 的情况下验证我是不是在给定的 GCD 队列上运行?