Ios 如何在Grand Central Dispatch中创建死锁?

Ios 如何在Grand Central Dispatch中创建死锁?,ios,objective-c,xcode,grand-central-dispatch,Ios,Objective C,Xcode,Grand Central Dispatch,在苹果文档中,它说: 重要提示:您不应调用调度同步或调度同步 从与您在同一队列中执行的任务执行函数 计划移交给职能部门。这对我们来说尤其重要 串行队列,保证死锁,但也应 避免使用并发队列 如何编写代码来实现这一点?创建死锁的简单代码: dispatch_queue_t q = dispatch_queue_create("deadlock queue", DISPATCH_QUEUE_SERIAL); NSLog(@"1"); dispatch_async(q, ^{ NSLog(@"

在苹果文档中,它说:

重要提示:您不应调用调度同步或调度同步 从与您在同一队列中执行的任务执行函数 计划移交给职能部门。这对我们来说尤其重要 串行队列,保证死锁,但也应 避免使用并发队列


如何编写代码来实现这一点?

创建死锁的简单代码:

dispatch_queue_t q = dispatch_queue_create("deadlock queue", DISPATCH_QUEUE_SERIAL);

NSLog(@"1");
dispatch_async(q, ^{
    NSLog(@"2");
    dispatch_sync(q, ^{
        NSLog(@"3");
    });
    NSLog(@"4");
});
NSLog(@"5");
日志输出:

1
5
2

这里,内部块被安排在串行队列
q
上运行,但在当前块完成之前,它无法运行,而当前块则依次等待内部完成,正如我们同步调用它一样

特定队列上的故意死锁:

dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{
        // outer block is waiting for this inner block to complete,
        // inner block won't start before outer block finishes
        // => deadlock
    });

    // this will never be reached
}); 

很明显,外部块和内部块在同一队列上运行。大多数情况下,这种情况发生在
dispatch\u sync
的调用者操作的队列不太明显的地方。这通常发生在一个(深度)嵌套堆栈中,在该堆栈中,您正在执行最初在某个队列上启动的某个类中的代码,并且意外地调用了一个
dispatch\u sync

阻止的最简单方法是在当前队列上执行
dispatch\u sync

dispatch_sync(dispatch_get_current_queue(), ^{});

当当前队列是串行队列(例如,主队列)时,此功能将被阻止。

在最新的Swift语法中:

let queue = DispatchQueue(label: "label")
queue.async {
    queue.sync {
        // outer block is waiting for this inner block to complete,
        // inner block won't start before outer block finishes
        // => deadlock
    }
    // this will never be reached
}
面试官经常问:“造成僵局的最简单方法是什么?”

Obj-C:

dispatch_sync(dispatch_get_main_queue(), ^{});
斯威夫特:

DispatchQueue.main.sync {}

从主线程调用
sync
将导致死锁,因为主队列是串行队列,
sync
将停止当前队列执行,直到传递的块/闭包完成。

在Swift 4.2中,可以使用以下代码段导致死锁:

let aSerialQueue = DispatchQueue(label: "my.label")

aSerialQueue.sync {
    // The code inside this closure will be executed synchronously.
    aSerialQueue.sync {
        // The code inside this closure should also be executed synchronously and on the same queue that is still executing the outer closure ==> It will keep waiting for it to finish ==> it will never be executed ==> Deadlock.
    }
}

如果有人好奇,那么如果针对同一队列调用了
sync
,则并发队列不会死锁。我知道这很明显,但我需要确认在我的例子中,只有串行队列的行为是这样的调度队列调用Xcode中的异常或设备上的崩溃,但是使用sleep()更适合我的测试目的(它只是当前队列中的冻结)


您想要创建死锁的代码示例吗?是的,有关学习,请参阅一个可以轻松死锁的实际示例。当处于死锁状态时。。。UI不应该是无响应的吗?@user1251004仅当主队列被阻止时才有效。在本例中,仅创建的
队列被阻止。主队列愉快地继续运行。是否有其他可能的方法在iOS中创建死锁?如果使用并发队列,会发生什么情况?非常感谢@Joriskluivers为什么内部块要等到外部块完成后才启动?@ScottyBlades,因为这是一个串行队列OK。知道了。异步块被添加到队列中,并在尝试执行时,它将命中同步函数,该函数在异步块完成执行之前不会启动,这是永远不会发生的,因为块需要到达其执行的结尾,而这是永远不会发生的,因为queue.sync正在等待另一个。这将在第3行给出EXC_BAD_指令
sleep(UInt32.max)