iPhone-大中央调度主线程


145

我一直在应用程序中成功使用大型中央调度程序,但是我想知道使用这样的东西的真正优势是什么:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

甚至

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

我的意思是,在两种情况下,您都将触发要在主线程上执行的块,确切地说是应用程序的运行位置,这无助于减少负载。在第一种情况下,您无法控制该块何时运行。我已经看到了在您触发它们后半秒钟执行块的情况。第二种情况,类似于

[self doStuff];

对?

我想知道你们的想法。


9
顺便说一句,将主队列扔到dispatch_sync中将导致死锁。
Brooks Hanes 2013年

5
只需在docs中阅读即可:“与dispatch_async不同,[dispatch_sync]直到块完成后才会返回。调用此函数并以当前队列为目标会导致死锁。” ...但是也许我读错了...(当前队列并不意味着主线程。如果我错了,请更正。
Brooks Hanes 2013年

4
@BrooksHanes并不总是正确的。如果您已经在主线程上,它将导致死锁。如果没有,那就不会有僵局。看到这里
亲爱的

Answers:


296

通常将块分配到主队列是从后台队列完成的,以表示某些后台处理已完成,例如

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

在这种情况下,我们将在后台队列上进行冗长的计算,并且需要在计算完成后更新UI。通常必须从主队列中完成UI的更新,因此我们使用第二个嵌套的dispatch_async将其“信号”回到主队列。

可能还有其他一些示例,您可能希望将其分派回主队列,但通常是通过这种方式完成的,即嵌套在分派给后台队列的块中。

  • 后台处理完成->更新UI
  • 在后台队列上处理的数据块->通知主队列以开始下一个块
  • 后台队列上的传入网络数据->通知主队列消息已到达

至于为什么可能要从主队列中分派到主队列中呢……好吧,尽管可以想象,您可能不会这样做以安排一些工作在下次运行循环中进行。


知道了 所以,我是对的。如果您已经在主队列中,就好像您在另一个队列中并想要更新UI一样,这样做没有任何好处。谢谢。

刚刚编辑了我的答案,以谈论为什么从主队列执行此操作不是很有用。
罗宾·萨默希尔

另外,我认为iOS 4中存在一个错误(iOS 5中可能已消失),其中从主线程到主队列的dispatch_sync只会导致挂起,因此我将完全避免这样做。
joerick 2011年

10
那不是错误,而是预期的行为。当然,这不是非常有用的行为,但是在使用dispatch_sync时,您始终需要注意死锁。您不能指望系统始终保护您免受程序员错误的侵害。
罗宾·萨默希尔

2
什么是backgroundQueue?我如何创建backgroundQueue对象
Nilesh Tupe 2012年

16

从主线程调度块到主队列可以是有用的。它使主队列有机会处理已排队的其他块,这样您就不会简单地阻止其他所有对象的执行。

例如,您可以编写一个本质上是单线程的服务器,该服务器仍然可以处理许多并发连接。只要队列中没有任何单个块花费太长时间,服务器就会保持对新请求的响应。

如果您的程序只花一生时间来响应事件,则什么都不做,那么这很自然。您只需将事件处理程序设置为在主队列上运行,然后调用dispatch_main(),就根本不必担心线程安全。


11

希望我能正确地理解您的问题,因为您想知道dispatch_async和dispatch_sync之间的区别?

dispatch_async

将异步地将块分配到队列。这意味着它将继续将块发送到队列,而不等待它返回,然后继续执行方法中剩余的代码。

dispatch_sync

将同步将块分配到队列。这将防止在该块执行完之前,不再执行该方法中的剩余代码。

我大部分dispatch_async时间都在后台队列中使用a 来摆脱主队列的工作,并利用设备可能具有的任何额外核心。然后dispatch_async到主线程,如果我需要更新UI。

祝好运


1
谢谢,但是我想问一下将内容发送到主队列中的好处。
鸭子

9

一个有用的地方是用于UI活动,例如在进行冗长的操作之前设置微调器:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

将无法正常工作,因为您在漫长的过程中阻塞了主线程,而没有让UIKit实际上启动微调器。

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

将控制权返回到运行循环,该循环将安排UI更新,启动微调器,然后使下一个事件脱离调度队列,这是您的实际处理。处理完成后,将调用动画停止,然后返回运行循环,然后在该循​​环中使用停止来更新UI。


@Jerceratops是的,但是它允许当前的运行循环完成。
Dan Rosenstark 2015年

3
是的,但是仍然很糟糕。它仍然阻止UI。我可能会在此按钮之后按另一个按钮。或尝试滚动。主线程上不应出现“(执行冗长的操作)”,并且不接受使按钮单击“完成”的dispatch_async。
Jerceratops,2015年


1

异步意味着异步,您应该在大多数时间使用异步。您永远不要在主线程上调用同步,因为在任务完成之前,它将锁定您的UI。您可以在Swift中执行以下操作:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

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.