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

Bla*_*use 20 xcode objective-c grand-central-dispatch ios

在Apple文档中,它说:

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

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

Jor*_*ers 36

某个队列上的故意死锁:

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
}); 
Run Code Online (Sandbox Code Playgroud)

这里很明显外部和内部块在同一队列上运行.发生这种情况的大多数情况都是在那些不太明显的操作调用者队列的地方dispatch_sync.这通常发生在(深层)嵌套堆栈中,您在某个类中执行代码,而这些类最初是在某个队列上启动的,并且偶然会调用dispatch_sync同一个队列.

  • @ user1251004仅在主队列被阻止时. (4认同)
  • 在此示例中,仅阻止创建的`queue`.主队列愉快地继续运行. (3认同)

Vla*_*mir 14

创建死锁的简单代码:

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");
Run Code Online (Sandbox Code Playgroud)

日志输出:

1
5
2
Run Code Online (Sandbox Code Playgroud)

这里内部块被安排在串行队列上运行,q但是在当前块完成之前它不能运行,而当前块又在内部完成,因为我们同步调用它.


Ale*_*toc 9

如果有人好奇,如果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")
Run Code Online (Sandbox Code Playgroud)

失败:

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


Nik*_*uhe 8

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

dispatch_sync(dispatch_get_current_queue(), ^{});
Run Code Online (Sandbox Code Playgroud)

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


小智 7

面试官经常问:“导致僵局的最简单方法是什么?”

对象-C:

dispatch_sync(dispatch_get_main_queue(), ^{});
Run Code Online (Sandbox Code Playgroud)

迅速:

DispatchQueue.main.sync {}
Run Code Online (Sandbox Code Playgroud)

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


Bar*_*zyk 5

在最新的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
}
Run Code Online (Sandbox Code Playgroud)

  • 好。得到它了。异步块被添加到队列中,并且在尝试执行时,它将触发同步功能,该功能直到异步块完成执行后才会启动,因为该块需要到达执行结束,所以永远不会发生这将永远不会发生,因为queue.sync正在等待另一个。 (3认同)