后台队列同步方法中的块在主线程上执行
Block in sync method on background queue is executed on main thread
刚开始学习 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 的队列中(通过查看标签)。 st运行ge,因为一般我在线程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
不会?我可以清楚地看到队列上的同步方法不应该在主线程上使用 运行,但为什么我的代码忽略了这种情况?
让我们考虑一下:
/// 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:)
invokes the work item on the thread which submitted it
因此,您的 queue
是后台队列这一事实并不重要。该块由调用sync()
的线程执行,该线程是主线程(调用viewDidLoad()
,调用dispatchFun()
)。
我认为您误读了 header 中的评论。这不是你是否从 发送 main
queue 的问题,而是你是否正在发送 到 的问题] main
queue.
因此,这是众所周知的 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 足够聪明,可以弄清楚“好吧,如果这个线程在我等待代码块 运行 时不做任何事情,我不妨在这里 运行 如果我可以,并将代价高昂的上下文切换保存到另一个线程。”
但在下面的场景中,我们在某些背景 queue 上做一些事情,并希望将其分派回 main
queue。在这种情况下,GCD 不会进行上述优化,而是始终 运行 将任务分派到 main
queue 上 main
queue:
// 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)
}
}
}
它这样做是因为某些事情必须 运行 在主 queue 上(例如 UI 更新),如果你要将它分派到主 queue,它将始终尊重该请求,并且不会尝试进行任何优化以避免上下文切换。
让我们考虑后一种情况的更实际的例子。
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`
}
}
}
现在,通常我们会在分派回主 queue 时使用 async
,但是 sync
header 文档中的注释只是让我们知道使用 sync
分派回主 queue 的任务实际上 运行 在主 queue 上,而不是在 URLSession
的后台 queue正如您可能担心的那样。
刚开始学习 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 的队列中(通过查看标签)。 st运行ge,因为一般我在线程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
不会?我可以清楚地看到队列上的同步方法不应该在主线程上使用 运行,但为什么我的代码忽略了这种情况?
让我们考虑一下:
/// 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:)
invokes the work item on the thread which submitted it
因此,您的 queue
是后台队列这一事实并不重要。该块由调用sync()
的线程执行,该线程是主线程(调用viewDidLoad()
,调用dispatchFun()
)。
我认为您误读了 header 中的评论。这不是你是否从 发送 main
queue 的问题,而是你是否正在发送 到 的问题] main
queue.
因此,这是众所周知的 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 足够聪明,可以弄清楚“好吧,如果这个线程在我等待代码块 运行 时不做任何事情,我不妨在这里 运行 如果我可以,并将代价高昂的上下文切换保存到另一个线程。”
但在下面的场景中,我们在某些背景 queue 上做一些事情,并希望将其分派回 main
queue。在这种情况下,GCD 不会进行上述优化,而是始终 运行 将任务分派到 main
queue 上 main
queue:
// 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)
}
}
}
它这样做是因为某些事情必须 运行 在主 queue 上(例如 UI 更新),如果你要将它分派到主 queue,它将始终尊重该请求,并且不会尝试进行任何优化以避免上下文切换。
让我们考虑后一种情况的更实际的例子。
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`
}
}
}
现在,通常我们会在分派回主 queue 时使用 async
,但是 sync
header 文档中的注释只是让我们知道使用 sync
分派回主 queue 的任务实际上 运行 在主 queue 上,而不是在 URLSession
的后台 queue正如您可能担心的那样。