Grand Central Dispatch (GCD) 与 performSelector - 需要更好的解释
Posted
技术标签:
【中文标题】Grand Central Dispatch (GCD) 与 performSelector - 需要更好的解释【英文标题】:Grand Central Dispatch (GCD) vs. performSelector - need a better explanation 【发布时间】:2011-07-10 15:42:51 【问题描述】:我在我的应用程序中同时使用了 GCD 和 performSelectorOnMainThread:waitUntilDone,并且倾向于认为它们可以互换——也就是说,performSelectorOnMainThread:waitUntilDone 是 GCD C 语法的 Obj-C 包装器。我一直认为这两个命令是等效的:
dispatch_sync(dispatch_get_main_queue(), ^ [self doit:YES]; );
[self performSelectorOnMainThread:@selector(doit:) withObject:YES waitUntilDone:YES];
我错了吗?也就是说, performSelector* 命令与 GCD 命令有区别吗?我已经阅读了很多关于它们的文档,但还没有看到明确的答案。
【问题讨论】:
withObject:YES 不起作用,至少应该给你一个警告。这可能是 GDC 的优势之一,您可以在其中向接收者发送任意参数。 对,我需要将它包装在一个 NSNumber 中。但是,忽略那部分,还有什么不同的吗?不过,好点子。 【参考方案1】:正如 Jacob 所指出的,虽然它们可能看起来相同,但它们是不同的东西。事实上,如果您已经在主线程上运行,它们处理向主线程发送操作的方式存在显着差异。
我最近遇到了这个问题,我有一个常用的方法,有时是从主线程上的某个东西运行的,有时不是。为了保护某些 UI 更新,我一直使用-performSelectorOnMainThread:
来处理它们,没有任何问题。
当我切换到在主队列上使用dispatch_sync
时,只要在主队列上运行此方法,应用程序就会死锁。阅读dispatch_sync
上的文档,我们看到:
调用此函数并定位 当前队列导致死锁。
我们看到-performSelectorOnMainThread:
的位置
等待
一个布尔值,指定是否 当前线程阻塞,直到 指定的选择器在 主线程上的接收器。指定 YES 阻塞这个线程;否则, 指定 NO 让此方法返回 马上。
如果当前线程也是主线程 线程,并为此指定 YES 参数,消息被传递 并立即处理。
我仍然更喜欢 GCD 的优雅,它提供的更好的编译时检查,以及它在参数方面的更大灵活性等,所以我制作了这个小辅助函数来防止死锁:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
if ([NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
更新:为了回应 Dave Dribin 指出 caveats section ondispatch_get_current_queue()
,我已更改为在上述代码中使用 [NSThread isMainThread]
。
然后我用
runOnMainQueueWithoutDeadlocking(^
//Do stuff
);
在主线程上执行我需要保护的操作,而不用担心原始方法在哪个线程上执行。
【讨论】:
@Joe - @Joe -dispatch_sync()
如果主线程上的某些东西在等待来自后台线程的值时被阻塞(通过dispatch_sync()
或其他方式),则主线程仍可能导致应用程序死锁)。这是非常不可能的,但仍有可能。这是两个线程等待另一个线程做某事的标准线程问题,所以什么都做不了。
迂腐溢出:您可以使用dispatch_block_t
作为参数类型,而不是丑陋的void (^block)(void)
。
@Adam - 自从我写下该部分后,该部分似乎已更新。它曾经告诉你,不能保证身份检查对dispatch_get_current_queue()
有效。
这已被添加到警告部分:It is equally unsafe for code to assume that synchronous execution onto a queue is safe from deadlock if that queue is not the one returned by dispatch_get_current_queue().
@BradLarson 使用dispatch_async(dispatch_get_main_queue(), block)
怎么样?有什么问题吗?【参考方案2】:
performSelectorOnMainThread:
不使用 GCD 向主线程上的对象发送消息。
documentation 说明该方法的实现方式如下:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
在performSelector:target:withObject:order:modes:
上,文档指出:
此方法设置一个计时器,在下一次运行循环迭代开始时在当前线程的运行循环上执行 aSelector 消息。定时器被配置为在模式参数指定的模式下运行。当计时器触发时,线程尝试将消息从运行循环中取出并执行选择器。如果运行循环正在运行并且处于指定模式之一,则成功;否则,计时器会一直等待,直到运行循环处于其中一种模式。
【讨论】:
【参考方案3】:GCD 的方式被认为更高效且更易于处理,并且仅适用于 ios4 及更高版本,而新旧 iOS 均支持 performSelector。
【讨论】:
以上是关于Grand Central Dispatch (GCD) 与 performSelector - 需要更好的解释的主要内容,如果未能解决你的问题,请参考以下文章
暂停和恢复 Grand Central Dispatch 线程
swift Grand Central Dispatch(GCD)发送信号量示例
NSOperation 与 Grand Central Dispatch
使用 Grand Central Dispatch (GCD) 创建恰好 N 个线程