iOS并发介绍

通过 谢尔盖·沙巴林 Sergey Shabalin)

假设您有一个运行在主线程上的移动应用程序,负责执行操作UI的代码。 每当您在应用中打包一些耗时的代码(例如从Web上下载或在主线程上进行图像处理)时,这都会显着降低UI性能,甚至可能导致其完全死机。

iOS中的多线程

那么有没有办法改变应用程序的架构,使此类问题永远不会发生? 我相信并发在这里可以提供帮助,因为并发能够执行两个或多个独立任务,例如计算,从Web或驱动器上下载数据,图像处理等。

但是,这并不是免费的,随着并发性的引入,代码线程安全性可能会在执行中受到损害。 一旦允许任务同时执行,就为任务可以访问相同资源的问题做好准备,例如更改不同线程上的相同变量或访问已被其他任务阻止的资源。 这最终可能导致拆除在不同线程上使用的那些资源。

在iOS开发中,并发用于提高生产力和UI响应能力,这是由Thread,GCD(中央总调度)和Operation等多种工具提供的。 可以肯定地说,在Swift 3出现之前,强大的GCD框架正在使用C API,这为用户操作带来了很多隐患。 Swift 3改变了一切。 GCD获得了一种基于GCD逻辑的易于使用的新语法。

为了更好地了解如何使用并发,让我们找出哪些关键概念与GCD和Operation工具一起使用。 基本概念是队列 。 因此,从开发人员的角度谈论iOS并发时,很可能会提到队列。 队列中有闭合的队列,并根据指定的顺序,系统将它们一个接一个地拉,然后将它们部署在适当的线程上。 队列在构成并发模式的多个线程中遵循FIFO(先进先出)原则。

串行队列与并发队列

队列可以是:

  • 串行或连续,当队列顶部的闭包被iOS拉出并运行直到结束时,再拉出另一个队列元素,依此类推。
  • 并发或多线程,当系统在队列顶部拉一个闭包并在某个线程中启动其执行时。

如果系统有权访问更多资源,则它将从队列中选择下一个元素,并在第一个功能仍在工作时在另一个线程上启动它。 这样,系统可以提取许多功能。

同步方法与异步方法

创建队列后,有两种将作业放入队列的方法:

  • 同步方法表示与当前队列有关的同步任务放置。 整个方法完成后, sync方法将控制权返回到当前队列,从而阻止了当前队列。
  • 异步方法是与当前队列相关的异步任务布置。 与sync方法相反, Async在另一个队列上启动任务后立即将控件返回到当前队列,而无需等待其结束。 因此, 异步方法不会阻止当前队列上的任务执行。

可能会出现以下不同的队列

  • 如果是异步执行方法, 则为 串行队列;如果是同步执行, 则为并发队列。
  • 并发队列。

开发人员的任务完全取决于队列选择,并在同步方法的帮助下将任务(封闭)同步添加到队列中,或通过异步方法异步添加到队列中。 iOS从那里获取它。

全局调度队列

除了定制的用户队列之外,iOS还提供了一些五种现成的全局调度队列:

  • 主队列。 负责所有UI操作:

let main = DispatchQueue.main

如果您需要执行某个会影响UI的函数或闭包,则此函数或闭包必须放在主队列中,因为这是第一优先级的全局调度队列。

  • 四个具有不同QoS功能和优先级的后台并发队列:

最高优先级:

let userInteractiveQueue = DispatchQueue.global(qos: .userInteractive)

let userInitiatedQueue = DispatchQueue.global(qos: .userInitiated)

let utilityQueue = DispatchQueue.global(qos: .utility)

最低优先级:

let backgroundQueue = DispatchQueue.global(.background)

默认优先级:

let defaultQueue = DispatchQueue.global()

苹果已经为每个队列配备了抽象QoS,并且必须为每个任务专门配置它。 以下是可用的QoS及其简要功能说明的列表:

  • .userInteractive是指与用户进行即时交互且几乎不需要时间的任务的质量。 动画执行是即时的,但不是在主队列上。 例如,当系统在该队列上执行图像处理计算时,用户在屏幕上滑动。 由于计算需要一些时间,因此刷卡结果会稍有延迟。 此队列的优先级很高,尽管它比主队列更受爱戴。
  • .userInitiated用于用户发起的任务,并且需要交互式事件之外的反馈。 用户需要反馈才能继续进行交互,该交互可能需要几秒钟的时间,具有较高的优先级,但低于前一个队列。
  • .utility用于需要一定时间才能执行且不需要即时反馈的任务,例如数据加载和数据库清理。 某些系统维护服务在无用户意图的情况下运行,这可能需要几分钟的时间。 优先级低于上一个队列。
  • .background用于与可视化无关且与执行时间无关的任务。 例如, 备份Web服务同步。 这些通常在不需要维护时在后台启动。 后台任务最多可能需要几个小时,并且在所有全局调度队列中具有最低的优先级。

还有一个名为的全局并发队列。 默认值 ,报告缺少有关QoS的信息。 该队列是通过DispatchQueue.global()运算符创建的。

并发问题

一旦任务被授予并发功能,它们就会争夺可用资源。 与此相关的三个主要问题是:

  • 比赛条件。 表示系统或应用程序操作取决于代码的执行方式时发生的多线程系统或应用程序投影错误。
  • 优先级倒置。 当与计划规则存在逻辑差异时,会发生此问题,因为较高优先级的任务正在挂起,而较低优先级的任务是根据队列继承执行的。
  • 僵局。 在多线程系统中,当多个线程处于无限等待状态的状态下,这就是这些线程占用的情况。

在iOS开发中,并发用于通过将任务划分为能够独立操作的不同线程来增强应用程序的性能。 但是,并发是一个困难的条件,需要深入分析要在任务和队列优先级方面进行优化的内容。