23

在 Apple 文档中,它说:

重要提示:您永远不应该从在您计划传递给函数的同一队列中执行的任务中调用 dispatch_sync 或 dispatch_sync_f 函数。这对于保证死锁的串行队列尤其重要,但对于并发队列也应该避免。

你如何编写代码来做到这一点?

4

8 回答 8

48

某个队列上的故意死锁:

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_sync正在操作哪个队列。这通常发生在(深度)嵌套堆栈中,您正在执行最初在某个队列上启动的某个类中的代码,并且意外地将 a 调用dispatch_sync到同一个队列中。

于 2013-03-13T09:35:34.313 回答
18

创建死锁的简单代码:

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但在当前块完成之前它不能运行,而当前块反过来等待内部完成,因为我们同步调用它。

于 2013-03-13T09:35:30.867 回答
9

最简单的阻塞方法是dispatch_sync在当前队列上:

dispatch_sync(dispatch_get_current_queue(), ^{});

当当前队列是串行队列时,这会阻塞,例如主队列。

于 2013-03-13T09:44:21.623 回答
7

在最新的 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
}
于 2017-12-18T08:22:07.847 回答
7

面试官经常会问:“造成死锁最简单的方法是什么?”

对象-C:

dispatch_sync(dispatch_get_main_queue(), ^{});

迅速:

DispatchQueue.main.sync {}

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

于 2018-03-11T18:46:46.757 回答
4

sync如果有人好奇,并发队列如果被称为针对同一队列,则不会死锁。我知道这很明显,但我需要确认只有串行队列的行为方式

作品:

let q = DispatchQueue(label: "myQueue", attributes: .concurrent)

q.async {
    print("work async start")
    q.sync {
        print("work sync in async")
    }
    print("work async end")
}

q.sync {
    print("work sync")
}

print("done")

失败:

初始化qlet q = DispatchQueue(label: "myQueue") // implicitly serial queue

于 2019-01-25T12:29:35.053 回答
2

在 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.
    }
}
于 2018-11-15T14:36:04.107 回答
0

在我的情况下,调度队列调用 Xcode 中的异常或设备崩溃,但sleep()的使用更适合我的测试目的(它只是当前队列中的冻结)。

sleep(UInt32.max)
于 2020-12-11T09:06:38.723 回答