质量服务类QoS

质量服务类

QoS允许我们对安排在NSOperation、NSOperationQueue、NSThread对象、分发队列(dispatch queue)或线程上的任务进行分类,通过分配QoS指定该任务的重要性,系统会通过优先级进行调度。

iOS 8之后才可以使用QoS

选择QoS类

因为系统利用QoS来调整调度、CPU以及I/O的优先级,通过这种机制使任务执行时在性能和能效上保持平衡。在选择QoS时需要考虑任务的可见性、重要性,主要有以下四个QoS类

Qos类任务类型以及Qos的重点工作时间
User-interactive用户交互与用户交互的工作,例如在主线程上操作的任务,如刷新用户界面或执行动画。 如果无法快速进行工作,则用户界面可能会冻结。 专注于响应能力和性能。工作几乎是瞬时的。
User-initiated用户启动用户已启动并需要立即获得结果的任务,例如打开保存的文档或用户单击用户界面中的控件时执行操作。 该任务执行是为了继续用户交互。 专注于响应能力和性能。工作几乎是瞬时的,例如几秒钟或更短的时间。
Utility实用程序这项工作可能需要一些时间才能完成,并且不需要立即取得结果,例如下载或导入数据。 实用程序任务通常有一个用户可见的进度栏提示用户进度。 专注于在响应能力、性能和能效之间取得平衡。工作需要几秒钟到几分钟。
Background后台程序在后台运行但对用户不可见的工作,例如建立索引,同步和备份。 专注于能源效率。工作需要花费大量时间,例如几分钟或几小时。

Optimally, run your app at a QoS level of utility or lower at least 90% of the time when user activity is not occurring.

On iPhones, discretionary and background operations, including networking, are paused when Low Power Mode is enabled. See React to Low Power Mode on iPhones.

特殊的QoS类

除了主要的QoS类之外,还有两种特殊的QoS类型,如下表。 在大多数情况下,开发中不会接触这些类,但是它们的存在具有价值。

QoS类描述
Default此QoS的优先级介于用户启动和实用程序之间。 开发人员无意使用此QoS对工作进行分类。 未分配QoS信息的工作被视为默认工作,并且GCD全局队列在此级别运行。
Unspecified这表示没有QoS信息,提示系统应该推断出环境QoS。如果线程使用可能使线程退出QoS的旧版API,因为它们可能未指定的QoS。线程可能使用Unspecified QoS。
为Operation和Queues指定QoS

如果开发中使用操作和队列来执行工作,则可以为执行的任务指定QoS。 NSOperation和NSOperationQueue都具有NSQualityOfService类型的qualityOfService属性,可以将其设置为以下值之一:-

  • NSQualityOfServiceUserInteractive

    对应上面表的User-interactive,用来处理用户操作,例如界面刷新、动画等。优先级最高,即时执行。

  • NSQualityOfServiceUserInitiated

    对应上面表的User-initiated,处理初始化任务,为将来的用户操作作准备。例如加载文件或 Email 等。基本即时执行,最多几秒延迟。

  • NSQualityOfServiceUtility

    对应上面表的Utility,用户不需要立即结果的操作,一般伴随进度条。例如下载、数据导入、周期性的内容更新等。几秒到几分钟延迟。

  • NSQualityOfServiceBackground

    对应上面表的Background,于用户不可见的操作。例如简历索引、预加载、同步等。几分钟到数小时延迟

  • NSQualityOfServiceDefault

    默认的 QoS 用来表示缺省值。当有可能通过其它途径推断出可能的 QoS 信息时,则使用推断出的 Qos。如果不能推断,则使用 UserInitiated 和 Utility 之间的 QoS。

NSOperation *myOperation = [[NSOperation alloc] init];
myOperation.qualityOfService = NSQualityOfServiceUtility;
NSOperationQueue 和 NSOperation的QoS的推断和提升

QoS并不是操作和队列的静态设置,它可能会随着时间的推移而波动,具体情况取决于各种标准。 例如,操作的QoS和队列的QoS不匹配,操作和从属操作不匹配,或者没有分配QoS的操作。 在这些情况下,可以推断出QoS。

许多规则控制关于队列(请参阅表4-3)和操作(请参阅表4-4)的QoS推断和提升的发生方式。

NSOperationQueue QoS的 推断和提升规则

情景结果
队列未分配QoS,并且将具有QoS的操作添加到队列中。队列及其其他操作(如果有的话)保持不受影响。
为队列分配了QoS,并将具有QoS的操作添加到队列中。如果新操作的QoS较高,则会提升队列的QoS。
队列中所有的具有较低QoS的操作都将提升。
具有较低QoS的任何操作如果添加到这个队列中的,它的QoS也将会提升
通过更改队列的qualityOfService属性的值来提高队列的QoS。队列中所有的较低QoS的操作都将提升为较高QoS。
将来添加到队列中的,具有较低QoS的任何操作都将提升为较高的QoS。
通过更改队列的qualityOfService属性的值来降低队列的QoS。队列中的任何操作均不受影响。
将来添加到队列中的任何操作都将推断出较低的QoS,除非为其分配了较高的QoS,在这种情况下,它们将保留其分配的QoS级别。

NSOperation QoS的 推断和提升规则

情景结果
没有为操作分配QoS该操作推断父操作,队列,[NSProcessInfo performActivityWithOptions:reason:usingBlock:]块或线程(如果有)的QoS。在主线程上创建操作的情况下,可以推断出NSQualityOfServiceUserInitiated的QoS。
具有QoS的操作将添加到具有较高QoS的队列中。提升操作的QoS以匹配队列的QoS。
包含操作的队列的QoS提升如果该队列的新QoS高于该操作的当前QoS,则该操作将推断出该队列的新QoS。
另一个操作依赖于(子级)操作(父级)。如果QoS较高,则父级操作会推断出子级操作的QoS。
通过更改操作的qualityOfService属性可以提高操作的QoS。该操作推断出新的QoS。

如果更高,则将任何子操作提升为新的QoS。

如果操作队列中位于操作前面的其他操作更高,则将其提升为新的QoS。

通过更改操作的qualityOfService属性可以降低操作的QoS。
通过更改操作的qualityOfService属性可以降低操作的QoS该操作推断出新的QoS。

任何子操作均不受影响。

操作队列不受影响。

调整运行操作的QoS

操作运行后,您可以通过以下方式之一更改其QoS:

  • 更改操作的qualityOfService属性。 请注意,这样做还会更改正在运行该操作的线程的QoS。

  • 将具有更高QoS的新操作添加到正在运行的操作的队列中。 这将提升正在运行的操作的QoS以匹配该操作的QoS。

  • 使用addDependency:将具有较高QoS的操作作为从属操作添加到正在运行的操作中。

  • 使用waitUntilFinished或waitUntilAllOperationsAreFinished。 这将提升正在运行的操作的QoS以匹配调用方的QoS。

为调度队列和块指定QoS

如果您的应用程序使用GCD,则可以将QoS类应用于分派队列和块。

1、调度队列
对于调度队列,在创建队列时调用dispatch_queue_attr_make_with_qos_class来指定QoS。 首先,为QoS创建一个调度队列属性,然后在创建队列时提供该属性,如下所示。

dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_UTILITY, 0);
dispatch_queue_t myQueue = dispatch_queue_create("com.YourApp.YourQueue", qosAttribute);

但是QoS是调度队列的不变属性,创建队列后就无法更改。 调用dispatch_queue_get_qos_class检索分配给调度队列的QoS。

qosClass = dispatch_queue_get_qos_class(myQueue, &relative);

下面表显示了GCD QoS类如何映射到Foundation QoS等效项。

GCD QoS classes (defined in sys/qos.h)Corresponding Foundation QoS classes
QOS_CLASS_USER_INTERACTIVENSQualityOfServiceUserInteractive
QOS_CLASS_USER_INITIATEDNSQualityOfServiceUserInitiated
QOS_CLASS_UTILITYNSQualityOfServiceUtility
QOS_CLASS_BACKGROUNDNSQualityOfServiceBackground

2、全局并发队列
之前,GCD提供了high,default,low和background全局并发队列,让开发者使用来对工作进行优先级排序。 现在使用相应的QoS类代替这些队列。 表4-6说明了这些队列及其对应的QoS类之间的映射。

Global queueCorresponding QoS class
Main threadUser-interactive
DISPATCH_QUEUE_PRIORITY_HIGHUser-initiated
DISPATCH_QUEUE_PRIORITY_DEFAULTDefault
DISPATCH_QUEUE_PRIORITY_LOWUtility
DISPATCH_QUEUE_PRIORITY_BACKGROUNDBackground

每个QoS类都存在一个全局并发队列。 要检索与给定QoS对应的全局并发队列,请调用dispatch_get_global_queue并将其传递给所需的QoS类。

utilityGlobalQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);

未分配QoS且未定位到全局并发队列的队列会系统会推断为unspecified QoS类。

3、Dispatch Blocks

GCD块API允许在块级别应用QoS类,例如在调用dispatch_async,dispatch_sync,dispatch_after,dispatch_apply或dispatch_once时。 创建代码块时,您可以执行此操作,如下所示。

dispatch_block_t myBlock;
myBlock = dispatch_block_create_with_qos_class(
     0, QOS_CLASS_UTILITY, -8, ^{…});
dispatch_async(myQueue, myBlock);
为线程指定QoS

1、普通线程

NSThread具有类型为NSQualityOfService的qualityOfService属性。 此类不会根据其执行的上下文推断QoS,因此只能在线程启动之前更改此属性的值。开发时 可以随时读取线程的qualityOfService值,线程会提供其当前值。

2、主线程和当前线程
根据其环境自动为主线程分配QoS。 在应用程序中,主线程以用户交互的QoS级别运行。 在XPC服务中,主线程以默认QoS运行。 调用qos_class_main函数检索主线程的QoS;调用qos_class_self函数检索当前正在运行的线程的QoS。

qosClass = qos_class_main();
qosClass = qos_class_self();
优先级倒置

当高优先级工作变得依赖于低优先级工作或成为低优先级工作的结果时,就会发生优先级倒置。结果,可能会发生阻塞,旋转和轮询。

  • 在同步工作的情况下,系统将尝试通过在反转期间提高较低优先级工作的QoS来自动解决优先级倒置问题。在以下情况下会发生这种情况:

    • 为串行队列上的块调用dispatch_sync()和dispatch_wait()时。
    • 当互斥量由具有较低QoS的线程持有时,调用pthread_mutex_lock()时。在这种情况下,将持有锁的线程提升到调用方的QoS。但是,跨多个锁不会发生此QoS提升。
  • 在异步工作的情况下,系统将尝试解决串行队列上发生的优先级倒置。

开发人员应确保不会写出发生优先级倒置的代码,也就不会强迫系统尝试解决问题。

调试服务质量等级

通过在Xcode中设置断点或在测试过程中暂停应用程序,您可以使用调试导航器中的CPU使用率表来检查您的应用程序,以确认是否正在应用请求的QoS类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的修养

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值