GCD在主线程中执行任务


254

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

我需要检查我是否已经在主线程上吗?或者通过调用下面的代码不执行此检查是否有任何损失?

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

114
五年后,我仍然不记得GCD块的语法,并且每次都到这里结束。
SpaceTrucker

7
@SpaceTrucker-那是我在此页面上的相同原因:D
马修·考利

4
9年后,我仍然要从此页面复制语法。
奥萨

Answers:


152

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

如果您已经在主线程上,则其行为是相同的:该块已调度,并在运行主线程的运行循环时执行。


3
问题是是否存在“通过不执行此检查而受到惩罚”的问题……我认为在不必要的情况下使用异步调度会产生性能损失,还是琐碎的?
丹·罗森斯塔克2011年

1
@Yar我认为在大多数情况下不会对性能产生明显影响:GCD是一个轻量级的库。就是说,我理解这个问题是:“考虑到下面的代码,是否需要检查我是否在主线程上?”

7
但是,您需要检查是否使用dispatch_sync。否则,您将陷入僵局。
维克托·恩格尔

如果您在主队列中并分派async回主队列,它将被运行,但是这可能会弄乱预期的操作时间。如UI代码viewDidLoad() 直到视图首次显示后才运行
pkamb

106

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

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

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

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

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
为什么不将其称为“ runOnMainQueueSync”呢?它可能会死锁而不会死锁的事实是我不想在我的所有代码中都遇到的那种情况。一如既往的感谢和+1。
丹·罗森斯塔克2011年

2
@Yar-我给它起了个名字,这样我就很清楚为什么我不只是在主队列上同步触发块,而不是使用此辅助函数。这更让我想起了自己。
布拉德·拉尔森

3
使用此功能仍然可能出现死锁。主队列同步>其他队列同步>主队列将死锁。
hfossli

2
@hfossli-是的,不会处理所有情况。在我的用法中,我总是从后台串行队列上的异步调度中调用,或者一直在主线程上进行内联代码。我想知道dispatch_set_specific()在您描述的情况下是否有帮助:stackoverflow.com/a/12806754/19679
布拉德·拉尔森

1
[NSThread isMainThread]通常返回YES,并且在GCD编程中检查这种情况并不安全。stackoverflow.com/questions/14716334/...
威尔Larche

57

正如提到的其他答案一样,来自主线程的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将无济于事。


需要说的是要考虑的重要事情
Marc-AlexandreBérubé16年

由于您要检查,因此这意味着代码可能会或可能不会在主线程中运行。主线程中已按该顺序运行,否则无法保证。因此,在引用多线程编程时,应避免将代码设计为按特定顺序运行。尝试使用异步回调方式。
哎呀,2016年

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.