ios - Dispatch Semaphore 会无意中死锁吗?

标签 ios grand-central-dispatch dispatchsemaphore

假设我们有一个共享资源,一堆不同的全局队列可以访问,为了这个问题,我们使用 Dispatch Semaphore 来管理该访问。当这些全局队列之一告诉信号量等待时,信号量计数会减少,并且该线程可以访问共享资源。是否有可能在信号量等待时,另一个(不同的)全局队列尝试访问此共享资源,而 GCD 从其池中抓取的线程与为前一个队列(当前正在制作的队列)抓取的线程相同信号量等待)这会死锁这个线程并防止信号量计数重新增加?

最佳答案

简短的回答:

是的,使用信号量可能会导致死锁,但不是出于您建议的原因。

长答案:

如果您有一些已调度的任务在等待信号量,则该工作线程将被阻塞,直到收到信号并恢复执行并随后返回。因此,您不必担心另一个分派(dispatch)的任务会尝试使用同一个线程,因为该线程会暂时从线程池中删除。您永远不必担心尝试同时使用同一个线程的两个分派(dispatch)任务。这不是死锁风险。

话虽如此,我们必须对线程池中工作线程的数量极为有限(目前每个 QoS 64 个)这一事实保持敏感。如果你用尽了可用的工作线程,那么任何其他分派(dispatch)到 GCD(具有相同 QoS)的东西都无法运行,直到一些以前阻塞的工作线程再次可用。

考虑:

print("start")

let semaphore = DispatchSemaphore(value: 0)
let queue = DispatchQueue.global()
let group = DispatchGroup()
let count = 10

for _ in 0 ..< count {
    queue.async(group: group) {
        semaphore.wait()
    }
}

for _ in 0 ..< count {
    queue.async(group: group) {
        semaphore.signal()
    }
}

group.notify(queue: .main) {
    print("done")
}

这很好用。您有 10 个工作线程与 wait 绑定(bind)。调用,然后额外的十个调度 block 调用 signal ,你很好。

但是,如果你增加 count到 100(称为“线程爆炸”的情况),上述代码将永远不会自行解决,因为 signal调用正在等待与所有这些 wait 绑定(bind)的工作线程来电。那些使用 signal 分派(dispatch)的任务都没有。电话将有机会运行。而且,当您耗尽工作线程时,这通常是一个灾难性问题,因为任何尝试使用 GCD(针对相同 QoS)的东西都将无法运行。

顺便说一句,在线程爆炸场景中使用信号量只是导致死锁的一种特殊方式。但是为了完整起见,值得注意的是,信号量有很多死锁的方法。最常见的例子是信号量(或调度组或其他)用于等待一些异步进程,例如
let semaphore = DispatchSemaphore(value: 0)
someAsynchronousMethod {
    // do something useful

    semaphore.signal()
}
semaphore.wait()

如果(a)您从主队列运行它,那可能会死锁;但是 (b) 异步方法也碰巧在主队列上调用了它的完成处理程序。这是典型的信号量死锁。

我只使用了上面的线程爆炸示例,因为死锁并不完全明显。但显然有很多方法可以导致信号量死锁。

关于ios - Dispatch Semaphore 会无意中死锁吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62054838/

相关文章:

ios - UIDatePicker 的今天变化

ios - 寻找有关如何将数据填充到具有两个 uiview 的 uitableviewcell 中的解决方案

cocoa - 如何使用 GCD 安全地锁定变量?

Swift 3 并行 for/map 循环

ios - 在Testflight上发布两个不同的版本

ios - 在后台线程中上传大图像。

ios - 如何在闭包中使用 DispatchSemaphore

swift - 为什么 DispatchSemaphore.wait() 会阻止这个完成处理程序?

iphone - CMMotionManager - 专门使用 CMMotionManager 获得航向