如何在没有死锁的情况下同步调度主队列?
我需要在主队列上同步分派一个块。 我不知道我目前是在主线程还是不在线。 天真的解决scheme如下所示:
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); }
这工作,但它是丑陋的。 在我至less把它隐藏在一些自定义函数之前,是不是有更好的解决这个问题的办法? 我强调,我不能asynchronous调度块 – 应用程序处于asynchronous调度块“执行太迟”的情况。
我需要在我的Mac和iOS应用程序中经常使用这样的东西,所以我使用了下面的帮助函数(最初在这个答案中描述过):
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()
,因为该函数的警告部分曾经警告不要使用这个身份testing,并且这个调用在iOS 6中已经被弃用了 。
对于主队列或主线程(不相同)上的同步,我使用:
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) } }
我最近开始在UI更新过程中遇到了一个死锁。 这导致了这个堆栈溢出的问题,这导致我实现了一个runOnMainQueueWithoutDeadlocking
型助手函数的基础上接受的答案。
然而,真正的问题是,当从一个块更新UI时,我错误地使用了dispatch_sync
而不是dispatch_async
来获取UI更新的主队列。 代码完成容易做到,事后也许很难注意到。
所以,对于读这个问题的其他人来说: 如果不需要同步执行 ,只需使用dispatch_**a**sync
就可以避免可能间歇性的死锁。