fra*_*ank 1 queue grand-central-dispatch ios swift
我写一个演示
let queue = DispatchQueue.global()
queue.async {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now(), qos: .default) {
//do sth in default queue
group.leave()
}
group.wait()
DispatchQueue.main.async { [weak self] in
//do sth in main queue
}
}
Run Code Online (Sandbox Code Playgroud)
然后我收到线程性能检查警告
以 QOS_CLASS_USER_INITIATED 运行的线程正在等待以 QOS_CLASS_DEFAULT 运行的较低 QoS 线程。研究避免优先级倒置的方法
我尝试打印它显示的外部队列的 qos
(lldb) print queue.qos
(Dispatch.DispatchQoS) $R0 = {
qosClass = unspecified //I think this should be default.
relativePriority = 0
}
Run Code Online (Sandbox Code Playgroud)
苹果文档错了?
class func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue
Run Code Online (Sandbox Code Playgroud)
你的问题的标题说,
\n\n\nDispatchQueue.global()默认qos是userInitiated
\n
考虑:
\nexamineQoS(qos: .default) // original QoS: default; queue QoS: DispatchQoS(qosClass: .unspecified, relativePriority: 0); thread.QoS: .userInitiated !!! default global queue is `.unspecified` and thread is `.userInitiated`\nexamineQoS(qos: .userInteractive) // original QoS: userInteractive; queue QoS: DispatchQoS(qosClass: .userInteractive, relativePriority: 0); thread.QoS: .userInteractive\nexamineQoS(qos: .userInitiated) // original QoS: userInitiated; queue QoS: DispatchQoS(qosClass: .userInitiated, relativePriority: 0); thread.QoS: .userInitiated\nexamineQoS(qos: .utility) // original QoS: utility; queue QoS: DispatchQoS(qosClass: .utility, relativePriority: 0); thread.QoS: .utility\nexamineQoS(qos: .background) // original QoS: background; queue QoS: DispatchQoS(qosClass: .background, relativePriority: 0); thread.QoS: .background\n\nfunc examineQoS(qos: DispatchQoS.QoSClass) {\n let queue = DispatchQueue.global(qos: qos)\n queue.async {\n print("original QoS:", qos, "; queue QoS:", queue.qos, "; thread.QoS:", Thread.current.qualityOfService)\n }\n}\n\nextension QualityOfService: CustomStringConvertible {\n public var description: String {\n switch self {\n case .userInteractive: return ".userInteractive"\n case .userInitiated: return ".userInitiated"\n case .utility: return ".utility"\n case .background: return ".background"\n case .default: return ".default"\n @unknown default: return "@unknown default"\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n请注意,global()(或global(qos: .default)) 实际上是创建一个 QoS 为 的队列.unspecified,因此其工作线程将继承调用者(即主队列.userInitiated)的 QoS。
好的,让\xe2\x80\x99s 在代码片段中执行相同的日志记录:
\nlet outerQueue = DispatchQueue.global(qos: .default)\nouterQueue.async {\n print("outerQueue:", outerQueue.qos, "; thread:", Thread.current.qualityOfService)\n\n // outerQueue: DispatchQoS(qosClass: .unspecified, relativePriority: 0) ; thread: .userInitiated\n\n let group = DispatchGroup()\n group.enter()\n let innerQueue = DispatchQueue.global()\n innerQueue.asyncAfter(deadline: DispatchTime.now(), qos: .default) {\n print("innerQueue:", innerQueue.qos, "; thread:", Thread.current.qualityOfService)\n\n // innerQueue: DispatchQoS(qosClass: .unspecified, relativePriority: 0) ; thread: .default\n\n //do sth in default queue\n group.leave()\n }\n group.wait()\n DispatchQueue.main.async { [weak self] in\n self?.doSomething()\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n因此,外部队列(从主队列调度的队列)获取了 QoS 的工作线程.userInitiated,但内部队列(从外部队列调度的队列)使用了 QoS 的工作线程.default(因为您.default在asyncAfter调用中明确指定了)。所以 \xe2\x80\x9cuser-initated\xe2\x80\x9d 线程正在等待 \xe2\x80\x9cdefault\xe2\x80\x9d 线程:优先级反转。
这就引出了一个问题:如何避免这种优先级倒置。
\n显然,可以为两个队列指定显式 QoS,确保不会有高 QoS 线程等待低 QoS 线程。
\n您可以从 中删除该qos参数asyncAfter,内部队列将从调用者那里继承 QoS,并且不会出现优先级反转。
更深入的观察是,应wait尽可能避免调用 。它效率低下并且占用了 GCD 工作线程之一(这是相当有限的)。如果您曾经从主队列中执行过此操作,则可能会产生相当严重的影响。
简而言之,wait我们应该notify:
let outerQueue = DispatchQueue.global(qos: .default)\nouterQueue.async {\n let group = DispatchGroup()\n group.enter()\n DispatchQueue.global().asyncAfter(deadline: .now()) {\n //do sth in default queue\n group.leave()\n }\n group.notify(queue: .main) { [weak self] in // NB: not `wait`\n self?.doSomething()\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n你继续问:
\n\n\n苹果文档错了?
\nRun Code Online (Sandbox Code Playgroud)\nclass func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue\n
从技术上讲,混乱的根源不是 的qos参数的默认值global(qos:)。即使明确指定 QoS 为.default,该队列\xe2\x80\x99s 底层qos类仍然是 \xe2\x80\x9cunspecified\xe2\x80\x9d,如上所示。
你说:
\n\n\n我认为这应该是\xe2\x80\x9cdefault\xe2\x80\x9d。
\n
我同意这种观点,但不是在 \xe2\x80\x9cuser-initerated\xe2\x80\x9d 和 \xe2\x80\x9cutility\xe2\x80\x9d 之间使用具有固定 QoS 的默认全局队列,而是让默认全局队列具有 \xe2\x80\x9cunspecified\xe2\x80\x9d QoS 可能是一个有意识的决定,即让它利用适合调用上下文的 QoS 的工作线程。例如,考虑:
\nDispatchQueue(label: "Background", qos: .background).async {\n DispatchQueue.global().async {\n // `Thread.current.qualityOfService` is background; that makes sense\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这种默认/未指定的全局队列行为可能是一个有意识的决定,以避免标准用例中此全局队列上不必要的优先级反转。这可能是设计好的\xe2\x80\x9cfeature\xe2\x80\x9d,而不是\xe2\x80\x9cbug\xe2\x80\x9d。(注意,这似乎仅限于默认的 QoS 全局队列;我们自己的默认 QoS 自定义队列不会体现此行为。)通过 libDispatch源代码,很难判断此行为是否是有意为之(因为没有我看到的代码明确强制执行了这种模式)或者它是否只是一个副作用(有意或无意)。
\n但我同意你的观点。如果是这样,那就更正确了:
\n.unspecified(假设没有指定;哈哈);和.default,则实际上应该是.default,而不是.unspecified。我已经为此开了一张票(尽管我不确定它会被接受,因为纠正这种行为并不完全向后兼容......至少他们可以在文档中澄清)。
\n旧版《iOS 应用程序能源效率指南》文档描述了默认和未指定的 QoS,如下所示:
\n\n\n默认
\n该 QoS 的优先级介于用户启动和实用之间。此 QoS 无意供开发人员用来对工作进行分类。没有分配 QoS 信息的工作被视为默认工作,GCD 全局队列在此级别运行。
\n未指定
\n这表示缺少 QoS 信息,并提示系统应推断环境 QoS。如果线程使用可能选择线程退出 QoS 的旧版 API,则线程可能具有未指定的 QoS。
\n
| 归档时间: |
|
| 查看次数: |
1596 次 |
| 最近记录: |