1

我最近一直在学习线程和 DispatchQueues 并且遇到了一个大问题。我多次听说 GCD不保证可以在哪个线程上执行给定的工作块。大多数时候,这是一个有用的层次抽象。但是,我遇到了一个我仍然不知道原因的错误,但这让我意识到在我看来这似乎是 GCD 这方面的一个潜在陷阱。

例子:

let queue1 = DispatchQueue(label: "one")
let queue2 = DispatchQueue(label: "two")
queue1.sync {
    let importantValue1 = "importantValue1"
    let importantValue2 = queue2.sync {
        return "importantValue2"
    }
    print("did important work, got values", importantValue1, importantValue2)
}

我的问题是,我是否至少可以保证我的队列不会在同一个线程上执行?从我所见,我似乎没有这个保证。但是,没有它,我不是一直处于僵局的危险之中吗?在上面的例子中,如果两个队列都在线程 7 上执行会发生什么?调用不会queue2.sync导致应用程序崩溃吗?

4

2 回答 2

1

后台队列中的任务永远不会阻止后台队列中的其他任务运行,因此虽然您可能会遇到死锁,但在同一线程上执行的队列不会导致死锁。

另一方面,在另一个队列上同步运行代码是毫无意义的。

于 2019-02-18T20:35:16.967 回答
1

在大多数情况下,我希望这两个块在同一个队列上运行。事实上,让我们看看:

import Foundation

let queue1 = DispatchQueue(label: "one")
let queue2 = DispatchQueue(label: "two")
queue1.sync {
    let importantValue1 = "importantValue1"
    print(Thread.current)  // Print the current queue
    let importantValue2: String = queue2.sync {
        print(Thread.current) // Print the current queue
        return "importantValue2"
    }
    print("did important work, got values", importantValue1, importantValue2)
}

<NSThread: 0x6000023b2900>{number = 1, name = main}
<NSThread: 0x6000023b2900>{number = 1, name = main}
did important work, got values importantValue1 importantValue2

是的,在我的示例中,两者都在主线程上运行,正如您通常希望它们那样。通常没有理由在调用时强加切换线程的巨大成本.sync。当前线程在块完成之前不能做任何事情,所以它也可以在当前线程上运行该块,只要没有任何限制(例如,提交到主队列的块必须在主线程)。

你是对的,如果你不小心,这可能会产生死锁,但这不是因为使用的线程。死锁是.sync循环使用所固有的。无论使用什么底层线程,队列仍然必须按特定顺序调度块,这就是最常造成死锁的原因。

于 2019-02-18T20:35:30.203 回答