ChatGPT解决这个技术问题 Extra ChatGPT

GCD 在主线程中执行任务

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

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

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});
五年后,我仍然不记得 GCD 块的语法,每次都到这里结束。
@SpaceTrucker - 这与我在此页面上的原因相同:D
9 年后,我仍然从这个页面复制语法。
并且要复制的代码在问题中,而不是在答案中!这就是为什么提出问题如此重要的原因。
:))) 差不多 10 年后...

佚名

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

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


问题是是否存在“不执行此检查的惩罚”......我认为在没有必要时使用异步调度会有性能损失,还是微不足道?
@Yar 我认为在大多数情况下不会对性能产生明显影响:GCD 是一个轻量级库。也就是说,我将问题理解为:'鉴于下面的代码,我是否需要检查我是否在主线程上?
但是,您确实需要检查您是否使用 dispatch_sync。否则你会陷入僵局。
如果您在主队列中并将 async 分派回主队列,它将运行,但可能会打乱您的操作的预期时间。例如 viewDidLoad() not running until after the view is first displayed 中的 UI 代码。
C
Community

对于您上面描述的异步调度情况,您不需要检查您是否在主线程上。正如 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/…
M
Michael Chinen

正如提到的其他答案,来自主线程的 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 不会帮助您。


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

不,你不需要检查你是否在主线程中。以下是在 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