【问题标题】:Difference between dispatch_async and dispatch_sync on serial queue?串行队列上的dispatch_async和dispatch_sync之间的区别?
【发布时间】:2013-11-06 20:59:21
【问题描述】:

我创建了一个这样的串行队列:

    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);

dispatch_async 这样调用有什么区别

 dispatch_async(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_async(_serialQueue, ^{ /* TASK 2 */ });

dispatch_sync 在这个串行队列上这样调用?

 dispatch_sync(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_sync(_serialQueue, ^{ /* TASK 2 */ });

我的理解是,无论使用哪种dispatch方法,TASK 1都会在TASK 2之前执行完成,对吗?

【问题讨论】:

    标签: ios multithreading grand-central-dispatch


    【解决方案1】:

    是的。使用串行队列保证任务的串行执行。唯一的区别是dispatch_sync 仅在块完成后才返回,而dispatch_async 在块添加到队列后返回并且可能未完成。

    此代码

    dispatch_async(_serialQueue, ^{ printf("1"); });
    printf("2");
    dispatch_async(_serialQueue, ^{ printf("3"); });
    printf("4");
    

    它可以打印241321431234,但1总是在3之前

    此代码

    dispatch_sync(_serialQueue, ^{ printf("1"); });
    printf("2");
    dispatch_sync(_serialQueue, ^{ printf("3"); });
    printf("4");
    

    它总是打印1234


    注意:对于第一个代码,它不会打印1324。因为printf("3") 是在 printf("2") 被执行之后调度的。并且一个任务只能在它被调度之后执行。


    任务的执行时间不会改变任何东西。这段代码总是打印12

    dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
    dispatch_async(_serialQueue, ^{ printf("2"); });
    

    可能发生的事情是

    • 线程 1:dispatch_async 到串行队列的耗时任务(任务 1)
    • 线程 2:开始执行任务 1
    • 线程 1:dispatch_async 另一个任务(任务 2)到串行队列
    • 线程 2:任务 1 已完成。开始执行任务 2
    • 线程 2:任务 2 已完成。

    你总是会看到12

    【讨论】:

    • 也可以打印2134和1243
    • 我的问题是我们为什么不按照正常的方式来做呢? printf("1");printf("2") ;printf("3") ;printf("4") - 与 dispatch_sync 相比
    • @androniennn 是第二个例子吗?因为其他一些线程可能同时运行dispatch_sync(_serialQueue, ^{ /*change shared data*/ });
    • @asma22 在多个线程/调度队列之间共享一个非线程安全的对象是非常有用的。如果您只访问串行队列中的对象,您就知道您正在安全地访问它。
    • 我的意思是串行执行。就同​​一队列中的其他任务而言,所有任务都是串行执行的。当然,它仍然可以与其他队列并发。可以同时调度和执行任务是 GCD 的重点。
    【解决方案2】:

    dispatch_syncdispatch_async 的区别很简单。

    在您的两个示例中,TASK 1 将始终在 TASK 2 之前执行,因为它是在它之前调度的。

    然而,在dispatch_sync 示例中,直到TASK 1 被调度并执行之后,你才会调度TASK 2。这称为"blocking"。您的代码会等待(或“阻塞”)任务执行。

    dispatch_async 示例中,您的代码不会等待执行完成。两个块都将分派(并入队)到队列,其余代码将继续在该线程上执行。然后在未来的某个时间点,(取决于已分派到您的队列的其他内容),Task 1 将执行,然后Task 2 将执行。

    【讨论】:

    • 我认为您的订单有误。第一个例子是async,它是非阻塞版本
    • 我已经编辑了你对我认为你的意思的回答。如果不是这种情况,请更改并澄清。
    • 如果在同一个队列中调用 dispatch_sync 和 dispatch_async 会怎样? (反之亦然)
    • 在串行队列上,两个任务仍然一个接一个地执行。在第一种情况下,调用者等待第一个块完成但不等待第二个块。在第二种情况下,调用者不等待第一个块完成,而是等待第二个块。但由于队列按顺序执行块,调用者有效地等待两者完成。
    • 一个块也可以在它自己的队列上做一个dispatch_async(添加稍后将执行的更多块); dispatch_sync 在自己的串行队列或主队列上会死锁。在这种情况下,调用者将等待原始块完成,而不是等待其他块。请记住:dispatch_sync 将块放在队列的末尾,队列执行代码直到该块完成,然后 dispatch_sync 返回。 dispatch_async 只是在队列末尾添加块。
    【解决方案3】:

    这都与主队列有关。有4个排列。

    i) 串行队列,异步调度:这里的任务会一个接一个地执行,但是主线程(对 UI 的影响)不会等待返回

    ii) 串行队列,调度同步:这里任务会一个接一个地执行,但是主线程(对 UI 的影响)会显示延迟

    iii) 并发队列,dispatch async :这里任务并行执行,主线程(对UI的影响)不会等待返回,会很流畅。

    iv) 并发队列,dispatch sync : 这里的任务会并行执行,但是主线程(对 UI 的影响)会出现延迟

    您选择并发队列还是串行队列取决于您是否需要前一个任务的输出用于下一个任务。如果依赖上一个任务,则采用串行队列,否则采用并发队列。

    最后,当我们完成业务时,这是一种回到主线程的方式:

    DispatchQueue.main.async {
         // Do something here
    }
    

    【讨论】:

      猜你喜欢
      • 2011-09-26
      • 1970-01-01
      • 2012-05-02
      • 2012-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      相关资源
      最近更新 更多