How to dispatch on main queue synchronously without a deadlock?

前端 未结 3 1392
盖世英雄少女心
盖世英雄少女心 2020-11-29 16:44

I need to dispatch a block on the main queue, synchronously. I don’t know if I’m currently running on the main thread or no. The naive solution looks like this:



        
相关标签:
3条回答
  • 2020-11-29 17:00

    For syncing on the main queue or on the main thread (that is not the same) I use:

    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)
        }
    }
    
    0 讨论(0)
  • 2020-11-29 17:11

    I recently began experiencing a deadlock during UI updates. That lead me this Stack Overflow question, which lead to me implementing a runOnMainQueueWithoutDeadlocking-type helper function based on the accepted answer.

    The real issue, though, is that when updating the UI from a block I had mistakenly used dispatch_sync rather than dispatch_async to get the Main queue for UI updates. Easy to do with code completion, and perhaps hard to notice after the fact.

    So, for others reading this question: if synchronous execution is not required, simply using dispatch_**a**sync will avoid the deadlock you may be intermittently hitting.

    0 讨论(0)
  • 2020-11-29 17:13

    I need to use something like this fairly regularly within my Mac and iOS applications, so I use the following helper function (originally described in this answer):

    void runOnMainQueueWithoutDeadlocking(void (^block)(void))
    {
        if ([NSThread isMainThread])
        {
            block();
        }
        else
        {
            dispatch_sync(dispatch_get_main_queue(), block);
        }
    }
    

    which you call via

    runOnMainQueueWithoutDeadlocking(^{
        //Do stuff
    });
    

    This is pretty much the process you describe above, and I've talked to several other developers who have independently crafted something like this for themselves.

    I used [NSThread isMainThread] instead of checking dispatch_get_current_queue(), because the caveats section for that function once warned against using this for identity testing and the call was deprecated in iOS 6.

    0 讨论(0)
提交回复
热议问题