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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10330679/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:05:25  来源:igfitidea点击:

How to dispatch on main queue synchronously without a deadlock?

objective-ciosobjective-c-blocksgrand-central-dispatch

提问by zoul

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:

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

dispatch_sync(dispatch_get_main_queue(), block);

But if I'm currently inside of a block running on the main queue, this call creates a deadlock. (The synchronous dispatch waits for the block to finish, but the block does not even start running, since we are waiting for the current one to finish.)

但是,如果我当前在主队列上运行的块内,则此调用会造成死锁。(同步调度等待块完成,但块甚至没有开始运行,因为我们正在等待当前的完成。)

The obvious next step is to check for the current queue:

显而易见的下一步是检查当前队列:

if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
    block();
} else {
    dispatch_sync(dispatch_get_main_queue(), block);
}

This works, but it's ugly. Before I at least hide it behind some custom function, isn't there a better solution for this problem? I stress that I can't afford to dispatch the block asynchronously – the app is in a situation where the asynchronously dispatched block would get executed “too late”.

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

回答by Brad Larson

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):

我需要在我的 Mac 和 iOS 应用程序中经常使用这样的东西,所以我使用以下帮助函数(最初在这个答案中描述):

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 functiononce warned against using this for identity testing and the call was deprecated in iOS 6.

我使用[NSThread isMainThread]而不是检查dispatch_get_current_queue(),因为该函数警告部分曾经警告不要使用它进行身份测试,并且该调用在 iOS 6 中已弃用

回答by JollyJinx

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

回答by pkamb

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.

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

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

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

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

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