iPhone - Grand Central Dispatch 主线程

iPhone - Grand Central Dispatch main thread

提问人:Duck 提问时间:10/26/2011 最后编辑:mfaaniDuck 更新时间:4/12/2019 访问量:145674

问:

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

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

甚至

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

我的意思是,在这两种情况下,您都在触发一个要在主线程上执行的块,正是应用程序运行的位置,这无助于减少负载。在第一种情况下,您无法控制块何时运行。我见过在你触发后半秒钟就被执行的案例。第二种情况,类似于

[self doStuff];

右?

我想知道你们怎么看。

iOS 并发 grand-central-dispatch dispatch-async

评论

9赞 Brooks Hanes 12/13/2013
顺便说一句,将主队列扔到dispatch_sync中会导致死锁。
5赞 Brooks Hanes 12/14/2013
只需在文档中阅读它:“与dispatch_async不同,[dispatch_sync] 在块完成之前不会返回。调用此函数并针对当前队列会导致死锁。...但也许我读错了......(当前队列不表示主线程)。如果我错了,请纠正。
4赞 mfaani 12/29/2016
@BrooksHanes并不总是正确的。如果您已经在主线程上,则会导致死锁。如果没有,那么就不会有僵局。看这里

答:

296赞 Robin Summerhill 10/27/2011 #1

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

- (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
  • 在后台队列上处理的数据块 - >信号主队列开始下一个块
  • 后台队列上的传入网络数据 -> 表示主队列消息已到达
  • 等等等等

至于为什么你可能想主队列调度到主队列......好吧,你通常不会这样做,尽管可以想象,你可能会这样做来安排一些工作,以便在下次运行循环中完成。

评论

0赞 Duck 10/27/2011
啊,我明白了。所以,我是对的。如果您已经在主队列中,那么这样做没有任何优势,只是当您在另一个队列中并且想要更新 UI 时。谢谢。
0赞 Robin Summerhill 10/27/2011
刚刚编辑了我的答案,以讨论为什么从主队列中执行此操作不是很有用。
0赞 joerick 10/27/2011
另外,我认为iOS 4中有一个错误(可能在iOS 5中已经出现),从主线程dispatch_sync到主队列只会导致挂起,所以我会完全避免这样做。
10赞 Robin Summerhill 10/27/2011
这不是错误,这是预期的行为。诚然,这不是非常有用的行为,但在使用dispatch_sync时,您始终需要注意死锁。你不能指望系统一直保护你免受程序员错误的影响。
2赞 Nilesh Tupe 8/11/2012
这里的 backgroundQueue 是什么?如何创建 backgroundQueue 对象
11赞 timthetoolman 10/27/2011 #2

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

dispatch_async

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

dispatch_sync

将块同步调度到队列。这将阻止在块完成执行之前再执行方法中的剩余代码。

我主要使用后台队列来处理主队列的工作,并利用设备可能具有的任何额外内核。如果我需要更新 UI,然后到主线程。dispatch_asyncdispatch_async

祝你好运

评论

1赞 Duck 10/27/2011
谢谢,但我问的是将一些东西发送到主队列的好处,在主队列上。
17赞 bames53 4/12/2012 #3

将块从主线程分派到主队列可能很有用。它使主队列有机会处理其他已排队的块,这样您就不会简单地阻止其他所有内容的执行。

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

如果你的程序什么都不做,只是花了整个生命来响应事件,那么这可能是很自然的。您只需将事件处理程序设置为在主队列上运行,然后调用 dispatch_main(),您可能根本不需要担心线程安全。

9赞 weaselfloss1 7/10/2014 #4

它有用的一个地方是用于 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。

评论

0赞 Dan Rosenstark 11/25/2015
@Jerceratops是的,但它允许当前的运行循环完成。
3赞 Jerceratops 11/26/2015
是的,但它仍然很糟糕。它仍然阻止 UI。我可能会在这之后立即按下另一个按钮。或者尝试滚动。“(做一些冗长的事情)”不应该发生在主线程上,dispatch_async让按钮点击“完成”不是一个可接受的解决方案。
0赞 Esqarrouth 12/6/2015 #5

Async 表示异步,您应该在大部分时间使用它。切勿在主线程上调用 sync,因为它会锁定您的 UI,直到任务完成。You Here 是 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

8赞 Niall Kiddle 2/16/2018 #6

斯威夫特 3、4 和 5

在主线程上运行代码

DispatchQueue.main.async {
    // Your code here
}