【问题标题】:Confused about Operation and OperationQueue QoS relationship对 Operation 和 OperationQueue QoS 关系感到困惑
【发布时间】:2021-02-04 17:51:28
【问题描述】:

OperationQueue 文档说明了 qualityOfService 属性:

This property specifies the service level applied to operation
objects added to the queue

但是,只需将下面的代码复制并粘贴到新的 Playground 中,就可以简单地检查这是否不正确。

import Foundation

let q = OperationQueue()
q.qualityOfService = .userInitiated

print("QUEUE", q.qualityOfService.rawValue)

let op = BlockOperation()
op.addExecutionBlock {
    print("OP", op.qualityOfService.rawValue)
}

q.addOperations([op], waitUntilFinished: true)

您将看到队列 QoS 级别为 25/.userInitiated 和操作 -1/.default

那么这是否意味着操作 QoS 级别相对于提升的队列 @9​​87654332@ 级别是 .default,或者它是 .default,尽管队列具有更高的 QoS 级别?

我实际上期望的是这两个值应该是相同的。

PS:我需要在Operation 中调用Process,而qualityOfService 的设置应该与队列/操作相同。

【问题讨论】:

  • 这很奇怪,因为 op.qualityOfService 属性的文档说明“此属性的默认值为 NSQualityOfServiceBackground,您应该尽可能保留该值。”您可以通过在设备上启用低功耗模式来检查它 - 如果系统解释为op.qualityOfService == .background,则在禁用低功耗模式之前不会执行操作。
  • @EugeneDudnyk - 首先,文档不正确。该操作的默认 QoS 是 default,如 Erik 的问题所示,而不是文档建议的 background。我已经提交了一份关于文档的错误报告。其次,我建议不要通过查看它是否在低功耗模式下运行来确定后台 QoS。低功耗并不意味着后台 QoS 调度或操作队列中的某些内容无法运行,只是它可能不会运行(直到您离开低功耗模式)。如果要查看 QoS 是什么,请参考线程的 qualityOfService 值。
  • @Rob,不确定您的第二个陈述,因为此处的文档 developer.apple.com/library/archive/documentation/Performance/… 指出以下内容:重要的是,至少 90% 的时间在实用程序的 QoS 级别或更低的情况下运行您的应用程序当用户活动没有发生时。在 iPhone 上,当启用低功耗模式时,自主和后台操作(包括网络)会暂停。请参阅对 iPhone 上的低功耗模式做出反应。”
  • @EugeneDudnyk - 是的。我刚刚测试了它。 (我真的不是说这听起来像我确信的那样刻薄,但你测试过你的说法了吗?)不要误会我的意思:我相信操作系统会减少运行的类型在后台/实用程序队列上(我当然希望如此!),但它似乎不像“打开低功耗模式并且分派到后台队列的任务无法运行”那么简单。
  • @EugeneDudnyk - 仅供参考,the documentation 已更正以反映操作的默认值为 .default,而不是 .background

标签: ios swift macos cocoa grand-central-dispatch


【解决方案1】:

操作的qualityOfService 指示操作本身是否需要指定特定的 QoS。但是-1/.default 实际上意味着它将只使用队列的 QoS(以及所使用的工作线程的 QoS)。我不会非常担心操作的 QoS。你关心的是它运行的线程的 QoS:

let q = OperationQueue()
q.qualityOfService = .userInitiated

print("CURRENT", Thread.current.qualityOfService.rawValue)     // CURRENT 33
print("QUEUE", q.qualityOfService.rawValue)                    // QUEUE 25

let op = BlockOperation {
    print("THREAD", Thread.current.qualityOfService.rawValue)  // THREAD 25
}

q.addOperations([op], waitUntilFinished: false)

如您所见,运行代码的线程的 QoS 正是您所期望的。


如果您愿意,您可以看到将操作的 QoS 更改为高于队列的值将如何影响运行它的工作线程的 QoS。因此,没有为操作指定 QoS 的后台 QoS 队列:

let q = OperationQueue()
q.qualityOfService = .background

print("CURRENT", Thread.current.qualityOfService.rawValue)    // CURRENT 33
print("QUEUE", q.qualityOfService.rawValue)                   // QUEUE 9

let op = BlockOperation()
op.addExecutionBlock {
    print("OP", op.qualityOfService.rawValue)                 // OP -1
    print("THREAD", Thread.current.qualityOfService.rawValue) // THREAD 9
}

q.addOperations([op], waitUntilFinished: false)

但是,如果需要,您可以为操作指定特定的 QoS,在这种情况下将其升级为更高的 QoS:

let q = OperationQueue()
q.qualityOfService = .background

print("CURRENT", Thread.current.qualityOfService.rawValue)    // CURRENT 33
print("QUEUE", q.qualityOfService.rawValue)                   // QUEUE 9

let op = BlockOperation()
op.qualityOfService = .userInitiated                          // change op’s QoS, and thus the worker thread to a higher QoS, too
op.addExecutionBlock {
    print("OP", op.qualityOfService.rawValue)                 // OP 25
    print("THREAD", Thread.current.qualityOfService.rawValue) // THREAD 25
}

q.addOperations([op], waitUntilFinished: false)

【讨论】:

  • 谢谢!另一方面,Process 在 OP 内部调用时是否也会继承当前线程/操作 QoS? Process.qualityOfService 方法完全没有文档记录。
  • 试一试,你会发现它并没有继承线程/操作的 QoS,但如果你为进程显式设置了 QoS,它确实尊重这一点。
  • 所以我基本上应该在操作中说process.qualityOfService = Thread.current.qualityOfService,只是为了确定。
  • 如果您真的需要子进程具有与操作相同的 QoS,那么,好吧,就这样做。两个观察结果:首先,在大多数情况下,指定 QoS 并不重要,默认值就可以了。其次,在重要的地方,当前线程的 QoS 可能不如子进程的功能目的和相对重要性那么显着。但是在你的场景中做任何有意义的事情。恕我直言,这没有硬性规定。
【解决方案2】:

documentation关于操作从队列中推断QoS(“服务质量推断和提升”部分)。

你的特殊情况在那里被描述为:

队列中任何具有较低 QoS 的操作都会被提升为较高的 QoS。 将来添加到队列中的任何具有较低 QoS 的操作都将推断出较高的 QoS。

但是,这并不意味着 op.qualityOfService 属性被 OperationQueue 修改,因为这会删除您分配给操作的 QoS。换句话说,op.qualityOfService 的值可能不代表提升的 QoS,仅代表您分配的 QoS(或 .default)。

根据swift的Operation.swiftimplementationop.queuePriority是代表队列中实际优先级的属性,毕竟应用了QoS推断和提升的复杂逻辑。您可以将其映射回 QoS,就像在我刚刚提到的代码中转发一样

【讨论】:

  • 既然一个操作没有队列就不能做任何事情,而且队列决定了操作的有效qos,我想知道为什么一个操作有它自己的qos。
  • 他们倾向于更高的 QoS - 如果将 QoS 高于队列的操作添加到队列中,则该操作将以其 QoS 执行,而不是队列的 QoS。
  • 哦,我明白了!好的,感谢您解决这个问题。
  • 但更高的 QoS 偏差仅在添加操作时才成立。最好阅读我提到的文档,因为当已经有操作的队列被分配了一些 QoS 时,情况会复杂得多。
  • 是的......仍然没有得到它。这对于它的本质来说太复杂了。我将手动从queue > operation > process/nstask 传递整个 QoS 设置以确保它是明确的。
猜你喜欢
  • 2018-03-05
  • 2021-11-28
  • 2015-11-29
  • 2020-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-09
  • 2019-11-19
相关资源
最近更新 更多