首页 文章

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

提问于
浏览
61

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

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 回答

  • 67

    我需要在我的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 .

  • 0

    为了在主队列或主线程上进行同步(不一样),我使用:

    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

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

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

    因此,对于其他人来说,阅读这个问题: if synchronous execution is not required ,只需使用 dispatch_**a**sync 就可以避免您可能会间歇性地发生的死锁 .

相关问题