我需要同步在主队列上调度一个块。我不知道我目前是在主线程上运行还是不运行。天真的解决方案看起来像这样:
dispatch_sync(dispatch_get_main_queue(), block);
但是如果我当前在主队列上运行的块中,则此调用会产生死锁。 (同步调度等待块完成,但块甚至没有开始运行,因为我们正在等待当前块完成。)
显而易见的下一步是检查当前队列:
if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
这很有效,但很难看。在我至少将它隐藏在一些自定义功能之前,是否有更好的解决方案来解决这个问题?我强调我不能异步调度块 - 应用程序处于异步调度块将“执行得太晚”的情况。
答案 0 :(得分:68)
我需要在我的Mac和iOS应用程序中经常使用这样的东西,所以我使用以下辅助函数(最初在this answer中描述):
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
您通过
致电runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
这几乎就是你在上面描述的过程,我和其他几位开发人员进行了交谈,他们为自己独立制作了类似的东西。
我使用[NSThread isMainThread]
而不是检查dispatch_get_current_queue()
,因为caveats section for that function曾警告过不要使用它进行身份测试和the call was deprecated in iOS 6。
答案 1 :(得分:2)
为了在主队列或主线程上进行同步(不一样),我使用:
import Foundation
private let mainQueueKey = UnsafeMutablePointer<Void>.alloc(1)
private let mainQueueValue = UnsafeMutablePointer<Void>.alloc(1)
public func dispatch_sync_on_main_queue(block: () -> Void)
{
struct dispatchonce { static var token : dispatch_once_t = 0 }
dispatch_once(&dispatchonce.token,
{
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil)
})
if dispatch_get_specific(mainQueueKey) == mainQueueValue
{
block()
}
else
{
dispatch_sync(dispatch_get_main_queue(),block)
}
}
extension NSThread
{
public class func runBlockOnMainThread(block: () -> Void )
{
if NSThread.isMainThread()
{
block()
}
else
{
dispatch_sync(dispatch_get_main_queue(),block)
}
}
public class func runBlockOnMainQueue(block: () -> Void)
{
dispatch_sync_on_main_queue(block)
}
}
答案 2 :(得分:0)
我最近在UI更新期间遇到了死锁。这引出了我的Stack Overflow问题,这导致我根据接受的答案实现了runOnMainQueueWithoutDeadlocking
类型的帮助函数。
但真正的问题是,当从块中更新UI时,我错误地使用了dispatch_sync
而不是dispatch_async
来获取UI更新的主队列。代码完成很容易,事后可能很难注意到。
因此,对于其他人阅读此问题:如果不需要同步执行,只需使用dispatch_**a**sync
即可避免您可能间歇性地发生的死锁。