刚开始学习GCD时,我就遇到了麻烦,因为在创建后台队列时,我的代码仍在主线程上运行。这是我的代码:
import UIKit
class ViewController: UIViewController {
let queue = DispatchQueue(label: "internalqueue", qos: .background)
override func viewDidLoad() {
super.viewDidLoad()
dispatchFun {
assert(Thread.isMainThread)
let x = UIView()
}
}
func dispatchFun(handler: @escaping (() -> ())) {
queue.sync {
handler()
}
}
}
(对我而言)令人惊讶的是,这段代码没有引发任何错误!我希望断言会失败。我希望代码不会在主线程上运行。在调试器中,我看到在构造x
实例时,我在线程1上的队列中(通过查看标签)。奇怪,因为通常我在线程1上看到主线程标签。我的队列是否安排在主线程(线程1)上?
当我将sync
更改为async
时,断言失败。我也希望sync
也会发生这种情况。下面是断言失败时线程的附加图像。 当我使用sync
而不是async
时,希望能看到完全相同的调试信息。
在Swift来源中阅读sync
描述时,我读到以下内容:
/// As an optimization, `sync(execute:)` invokes the work item on the thread which
/// submitted it, except when the queue is the main queue or
/// a queue targetting it.
再次:除非队列是主队列
为什么后台调度队列上的sync
方法将代码放在主线程上运行,而async
却没有?我可以清楚地了解到,不应在主线程上运行队列上的sync方法,但是为什么我的代码会忽略这种情况?
答案 0 :(得分:2)
让我们考虑一下
/// As an optimization, `sync(execute:)` invokes the work item on the thread which
/// submitted it, except when the queue is the main queue or
/// a queue targetting it.
您要在自己的队列中调用sync()
。该队列是主队列还是以主队列为目标?不,这不对。因此,该异常无关紧要,只有这一部分是:
sync(execute:)
在提交它的线程上调用工作项
因此,您的queue
是后台队列这一事实无关紧要。该块由调用sync()
的线程(即主线程(称为viewDidLoad()
,称为dispatchFun()
)执行。
答案 1 :(得分:2)
我相信您在标题中误读了该评论。这不是您是否要从 因此,这是众所周知的 使用 但是在以下情况下,我们正在某些后台队列上执行某些操作,并希望将其分派回 之所以这样做,是因为某些事情必须在主队列上运行(例如UI更新),并且如果您将其分派到主队列,它将始终满足该请求,而不尝试执行任何操作优化以避免上下文切换。 让我们考虑一下后一种情况的更实际的例子。 现在,通常我们在分派回主队列时会使用main
队列调度 的问题,而是您是否要向main
队列调度的问题。 / p>
sync
优化,其中分派的块将在当前线程上运行:let backgroundQueue = DispatchQueue(label: "internalqueue", attributes: .concurrent)
// We'll dispatch from main thread _to_ background queue
func dispatchingToBackgroundQueue() {
backgroundQueue.sync {
print(#function, "this sync will run on the current thread, namely the main thread; isMainThread =", Thread.isMainThread)
}
backgroundQueue.async {
print(#function, "but this async will run on the background queue's thread; isMainThread =", Thread.isMainThread)
}
}
sync
时,您在告诉GCD“嘿,让该线程等待,直到另一个线程运行此代码块”。因此,GCD非常聪明,可以找出“好吧,如果在等待代码块运行时该线程不做任何事情,那么我也可以在这里运行它,并节省昂贵的上下文切换到另一个线程。” main
队列。在这种情况下,GCD将不会执行上述优化,而是将始终运行在main
队列上分配给main
队列的任务:// but this time, we'll dispatch from background queue _to_ the main queue
func dispatchingToTheMainQueue() {
backgroundQueue.async {
DispatchQueue.main.sync {
print(#function, "even though it’s sync, this will still run on the main thread; isMainThread =", Thread.isMainThread)
}
DispatchQueue.main.async {
print(#function, "needless to say, this async will run on the main thread; isMainThread =", Thread.isMainThread)
}
}
}
func performRequest(_ url: URL) {
URLSession.shared.dataTask(with: url) { data, _, _ in
DispatchQueue.main.sync {
// we're guaranteed that this actually will run on the main thread
// even though we used `sync`
}
}
}
async
,但是sync
标头文档中的注释只是让我们知道此任务是使用以下方法分派回主队列的sync
实际上将在主队列上运行,而不是您可能担心的在URLSession
的后台队列上运行。