63

我需要在主队列上同步调度一个块。我不知道我当前是否在主线程上运行。天真的解决方案如下所示:

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

这有效,但它很丑。在我至少将它隐藏在一些自定义函数后面之前,对于这个问题没有更好的解决方案吗?我强调我不能异步调度块——应用程序处于异步调度块将“太晚”执行的情况。

4

3 回答 3

69

我需要在我的 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(),因为该函数的警告部分曾经警告不要使用它进行身份测试,并且该调用在 iOS 6 中已弃用

于 2012-04-26T21:14:55.400 回答
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)
    }
}
于 2016-06-29T13:24:25.437 回答
0

我最近开始在 UI 更新期间遇到死锁。这导致了我这个 Stack Overflow 问题,这导致我runOnMainQueueWithoutDeadlocking根据接受的答案实现了一个 -type 辅助函数。

然而,真正的问题是,当从我错误使用的块更新 UI 时,dispatch_sync而不是dispatch_async获取 UI 更新的主队列。代码完成很容易做到,事后可能很难注意到。

因此,对于阅读此问题的其他人:如果不需要同步执行,只需使用即可dispatch_**a**sync避免您可能间歇性遇到的死锁。

于 2016-03-28T17:50:34.810 回答