为什么在当前队列中调用dispatch_sync不会导致死锁

时间:2015-07-01 09:15:31

标签: ios grand-central-dispatch deadlock

苹果文件说:( concurrencyProgrammingGuide,page49) 要点:您永远不应该从正在计划传递给该函数的同一队列中执行的任务调用dispatch_sync或dispatch_sync_f函数。这对于保证死锁的串行队列尤其重要,但对于并发队列也应避免使用。

但是这里的代码不会导致死锁,因为我已多次运行它:

    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^(){
    NSLog(@"in outer queue: %@", [NSThread currentThread]);
    dispatch_sync(concurrentQueue, ^(){
        NSLog(@"do someting thread: %@", [NSThread currentThread]);
    });
});

然而,我们都知道,在主线程上下文中,如果我们执行下面的代码,它将导致主线程中的死锁。所以我很困惑为什么在同一个线程中调用dispatch_sync,一个不是死锁(上面的代码),另一个相反(下面的代码)?

        dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"________update__UI");
    });

2 个答案:

答案 0 :(得分:4)

dispatch_get_global_queue()返回系统定义的全局并发队列。

答案 1 :(得分:3)

Serial Dispatch Queue(主队列和用户使用默认标志创建的队列)仅使用一个线程。 Concurrent Dispatch Queue(全局队列,使用并发标志创建的队列)使用多个线程(也就是线程池)。线程数随系统,情况而变化。

enter image description here

请查看以下代码。

dispatch_async(queue, ^(){

    /* Task 1 */

    dispatch_sync(queue, ^(){

        /* Task 2 */

    });
});

任务1和任务2应按与排队时相同的顺序执行。因此,执行任务1,然后执行任务2。

在Serial Dispatch Queue上,dispatch_sync必须等待才能在正在执行任务1的线程上执行任务2。的 DEADLOCK

在Concurrent Dispatch Queue上,dispatch_sync通常不需要等待在线程池中的线程上执行任务2。但是线程池中的线程数实际上并不是无限制的,有时候dispatch_sync必须等到其他任务完成。这就是" 的原因,但对于并发队列也应该避免"。 dispatch_sync也是高度优化的,它在某些情况下使用任务2的任务1的相同线程。

<强> EDITED

因此,dispatch_sync一个块意味着与普通块(函数)调用完全相同。在这种情况下,DEADLOCK从未发生过。

<强> EDITED

测试代码。

#import <Foundation/Foundation.h>

void task2()
{
    NSLog(@"task2: %@", [NSThread currentThread]);
}

void task1(dispatch_queue_t q)
{
    NSLog(@"task1: %@", [NSThread currentThread]);
    dispatch_sync(q, ^{
        task2();
    });
}

int main()
{
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    dispatch_async(q, ^{
        task1(q);
    });
    dispatch_main();
    return 0;
}

lldb log

(lldb) breakpoint set -l 6
(lldb) run
task1: <NSThread: 0x1001155a0>{number = 2, name = (null)}
task2: <NSThread: 0x1001155a0>{number = 2, name = (null)}

Process stopped

(lldb) bt
* thread #2: tid = 0x4dbcc, 0x0000000100000d34 a.out`task2 + 4 at a.m:5, queue = 'com.apple.root.default-qos', stop reason = breakpoint 1.1
  * frame #0: 0x0000000100000d34 a.out`task2 + 4 at a.m:5
    frame #1: 0x0000000100000dc5 a.out`__task1_block_invoke(.block_descriptor=<unavailable>) + 21 at a.m:12
    frame #2: 0x00007fff8d6d6c13 libdispatch.dylib`_dispatch_client_callout + 8
    frame #3: 0x00007fff8d6e19a1 libdispatch.dylib`_dispatch_sync_f_invoke + 39
    frame #4: 0x0000000100000da3 a.out`task1(q=0x00007fff79749b40) + 67 at a.m:11

task1函数通过libdispatch API调用task2函数,但它与普通函数调用几乎相同。