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

Ios 为什么在当前队列中调用dispatch_sync不会导致死锁,ios,grand-central-dispatch,deadlock,Ios,Grand Central Dispatch,Deadlock,苹果的文件说:(并发程序设计指南,第49页) 重要提示:决不能从计划传递给函数的同一队列中执行的任务调用dispatch_sync或dispatch_sync函数。这对于保证死锁的串行队列尤其重要,但对于并发队列也应该避免 但是这里的代码不会导致死锁,因为我已经运行了很多次: dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_

苹果的文件说:(并发程序设计指南,第49页) 重要提示:决不能从计划传递给函数的同一队列中执行的任务调用dispatch_sync或dispatch_sync函数。这对于保证死锁的串行队列尤其重要,但对于并发队列也应该避免

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

    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\u get\u global\u queue()
返回系统定义的全局并发队列。

串行调度队列(带有默认标志的主队列和用户创建的队列)仅使用一个线程。并发调度队列(全局队列,使用并发标志创建的队列)使用多个线程(也称为线程池)。线程数随系统、情况而变化

看看下面的代码

dispatch_async(queue, ^(){

    /* Task 1 */

    dispatch_sync(queue, ^(){

        /* Task 2 */

    });
});
任务1和任务2的执行顺序应与排队顺序相同。因此,先执行任务1,然后执行任务2

在串行调度队列中,
Dispatch\u sync
必须等待,才能在当前正在执行任务1的线程上执行任务2死锁

在并发调度队列中,
Dispatch\u sync
通常不需要等待在线程池中的线程上执行任务2。但是线程池中的线程数量实际上并不是无限的,有时
dispatch\u sync
必须等待其他任务完成。这就是为什么“,但对于并发队列也应该避免”
dispatch_sync
也是高度优化的,在某些情况下,它将任务1的同一线程用于任务2

已编辑

因此,
dispatch\u sync
a块的意思与普通块(函数)调用完全相同。在这种情况下,死锁从未发生过

已编辑

测试代码

#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;
}
#导入
void task2()
{
NSLog(@“task2:%@,[NSThread currentThread]);
}
无效任务1(调度队列)
{
NSLog(@“task1:%@,[NSThread currentThread]);
调度同步(q^{
task2();
});
}
int main()
{
dispatch_queue_t q=dispatch_get_global_queue(0,0);
调度异步(q^{
任务1(q);
});
调度_main();
返回0;
}
lldb日志

(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
(lldb)断点集-l 6
(lldb)运行
任务1:{number=2,name=(null)}
任务2:{number=2,name=(null)}
进程已停止
(lldb)英国电信
*线程#2:tid=0x4dbcc,0x0000000100000d34 a.out`task2+4 at a.m:5,queue='com.apple.root.default qos',停止原因=断点1.1
*帧#0:0x0000000100000d34 a.out`task2+4在上午:5
帧#1:0x0000000100000dc5 a.out`uuu task1_block_invoke(.block_descriptor=)+21在上午12点
帧2:0x00007fff8d6d6c13 libdispatch.dylib`\u dispatch\u client\u callout+8
帧3:0x00007fff8d6e19a1 libdispatch.dylib`\u dispatch\u sync\u f\u invoke+39
帧#4:0x0000000100000da3 a.out`task1(q=0x00007fff79749b40)+67在上午11点

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

而且,为了完整起见,应该指出主线程队列是串行队列。正如您所说,在并发队列上,task1和task2可能由不同的线程执行,因为存在线程池。但是,我尝试在task1和task2中打印[NSThread currentThread],它总是打印同一个线程:*.\uuuuuuuuuuuuuu task1:{number=2,name=(null)}.\uuuuuuuuuuuuuuuuuuu task2:{number=2,name=(null)}**,它不应该导致死锁吗?1.dispatch_async queue块,然后新线程a将执行该块;2.调度同步块线程A 3.由于线程A被阻止,task2将永远等待。但实际上它不会导致死锁老实说,我不知道有什么情况下,
dispatch\u sync
使用了与并发队列上当前线程不同的线程。因此,我猜
dispatch\u sync
可能总是使用相同的线程。因此,
dispatch\u sync
a块的意思与普通块(函数)调用完全相同。确实没有死锁。添加了一个
dispatch\u sync
作为普通函数调用的证据。
(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