如何在没有死锁的情况下同步调度主队列?

时间:2012-04-26 09:28:30

标签: objective-c ios objective-c-blocks grand-central-dispatch

我需要同步在主队列上调度一个块。我不知道我目前是在主线程上运行还是不运行。天真的解决方案看起来像这样:

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);
}

这很有效,但很难看。在我至少将它隐藏在一些自定义功能之前,是否有更好的解决方案来解决这个问题?我强调我不能异步调度块 - 应用程序处于异步调度块将“执行得太晚”的情况。

3 个答案:

答案 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即可避免您可能间歇性地发生的死锁。