【问题标题】:GCD to perform task in main threadGCD 在主线程中执行任务
【发布时间】:2011-08-05 11:07:24
【问题描述】:

我有一个可能来自任何线程的回调。当我得到这个回调时,我想在主线程上执行某个任务。

我是否需要检查我是否已经在主线程上 - 或者在调用下面的代码之前不执行此检查是否会受到任何惩罚?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

【问题讨论】:

  • 五年后我仍然不记得 GCD 块的语法,每次都到这里结束。
  • @SpaceTrucker - 这与我在此页面上的原因相同:D
  • 9年过去了,我还是来复制这个页面的语法。
  • 而要复制的代码在问题中而不是在答案中!这就是提问如此重要的原因。
  • :))) 差不多 10 年后...

标签: objective-c grand-central-dispatch


【解决方案1】:

不,你不需要检查你是否在主线程中。以下是在 Swift 中执行此操作的方法:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

它作为标准功能包含在我的仓库中,请查看:https://github.com/goktugyil/EZSwiftExtensions

【讨论】:

    【解决方案2】:

    正如提到的其他答案,主线程中的 dispatch_async 很好。

    但是,根据您的用例,您可能会考虑一个缺点:由于该块被安排在一个队列中,它不会执行,直到控制返回到运行循环,这将具有延迟块执行的影响。

    例如,

    NSLog(@"before dispatch async");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"inside dispatch async block main thread from main thread");
    });
    NSLog(@"after dispatch async");
    

    将打印出来:

    before dispatch async
    after dispatch async
    inside dispatch async block main thread from main thread
    

    因此,如果您希望块在外部 NSLog 之间执行,则 dispatch_async 不会帮助您。

    【讨论】:

    • 不得不说这是要考虑的重要事情
    • 既然要检查,这意味着代码可能会或可能不会在主线程中运行。已经在主线程中将按该顺序运行,否则无法保证。因此,在提及多线程编程时,您应该避免将代码设计为按特定顺序运行。尝试使用异步回调方式。
    【解决方案3】:

    对于您上面描述的异步调度情况,您不需要检查您是否在主线程上。正如 Bavarious 所指出的,这将简单地排队等待在主线程上运行。

    但是,如果您尝试使用dispatch_sync() 执行上述操作并且您的回调位于主线程上,那么您的应用程序将在此时死锁。我在回答here 中对此进行了描述,因为当从-performSelectorOnMainThread: 移动一些代码时,这种行为让我感到惊讶。正如我在那里提到的,我创建了一个辅助函数:

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

    如果您所在的方法当前不在主线程上,它将在主线程上同步运行一个块,如果是,则仅内联执行该块。您可以使用如下语法来使用它:

    runOnMainQueueWithoutDeadlocking(^{
        //Do stuff
    });
    

    【讨论】:

    • 为什么不把它叫做“runOnMainQueueSync”呢?它可能会死锁并且不会死锁的事实是我不想在我的代码中拥有的那种东西。谢谢,一如既往地 +1。
    • @Yar - 我只是给它起了这个名字,这样我就很清楚为什么我不只是在主队列上同步触发块而不是使用这个辅助函数。这更像是对我自己的提醒。
    • 这个函数还是有可能死锁的。主队列同步> 其他队列同步> 主队列会死锁。
    • @hfossli - 确实,它不会处理所有情况。在我的使用中,我总是从后台串行队列上的异步调度或主线程上的内联代码调用。我想知道dispatch_set_specific() 是否会对您描述的情况有所帮助:stackoverflow.com/a/12806754/19679
    • [NSThread isMainThread] 通常返回 YES 并且在 GCD 编程中检查这种情况被认为是不安全的。 stackoverflow.com/questions/14716334/…
    【解决方案4】:

    不,你不需要检查你是否已经在主线程上。通过将块分派到主队列,您只是将块安排在主线程上串行执行,这发生在相应的运行循环运行时。

    如果你已经在主线程上,行为是一样的:block被调度,并在主线程的run loop运行时执行。

    【讨论】:

    • 问题是是否存在“不执行此检查的惩罚”...我认为在没有必要时使用异步调度会导致性能损失,还是微不足道?
    • @Yar 我认为在大多数情况下不会对性能产生明显影响:GCD 是一个轻量级库。也就是说,我将问题理解为:'鉴于下面的代码,我是否需要检查我是否在主线程上?'
    • 但是,你需要检查你是否使用了 dispatch_sync。否则你会陷入僵局。
    • 如果您在主队列中并将async 分派回主队列,它将运行,但可能会扰乱您的操作的预期时间。比如viewDidLoad()not running until after the view is first displayed中的UI代码。
    猜你喜欢
    • 2021-11-17
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多