我最近一直在学习线程和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
导致应用崩溃吗?
后台队列中的任务永远不会阻止后台队列中的其他任务运行,因此当您可以获得死锁时,在同一线程上执行的队列将不会导致它。
另一方面,在另一个队列上同步运行代码是没有意义的。
在大多数情况下,我希望这两个块在同一队列上运行。事实上,让我们看看:
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
所固有的。无论使用什么底层线程,队列仍然必须按特定顺序调度块,这是最常见的死锁。