使用Swift设计多线程应用程序

作为汽车行业的iOS开发人员,我花了大量时间处理实时数据。 在当今的许多应用中,有效处理连续数据流的需求非常重要。 为确保不锁定用户界面,您很可能需要使用多线程。

实时流式传输的信息最有趣,因为您将不断收到可用于更新视觉效果的新数据。 这也是您最困难,最令人沮丧的事情,因为iOS设备在硬件方面有一定的局限性。 幸运的是,Apple通过称为GCD(大中央调度)的极其易于使用的界面提供了多线程。 您可能熟悉看起来像这样的代码:

如果您没有将主程序代码显式放置在另一个队列中,那么它将在其中运行大多数程序代码。 这是一个串行队列,这意味着它将选择行中的第一个项目,执行代码,等待它完成,然后释放该项目,然后选择行中的下一个项目,依此类推。

但是,主队列并不是通过GCD可用的唯一队列。 有许多预定义的队列都具有不同的优先级。 您还可以通过以下方式创建自己的专用队列:

请注意,我们刚刚创建的队列具有.concurrent属性,这意味着该特定队列在执行下一项之前不会等待一项完成。 它将简单地将第一个项目放在线程中并启动它,然后继续进行下一个项目,而不管第一个项目是否完成。

假设您正在处理采样率约为20Hz的数据流。 这意味着您将有大约50毫秒的时间来解析和解释数据,将其添加到数据结构中,并告诉视图进行显示。 如果您的iOS设备尝试在主线程上执行此操作,则将几乎没有时间检查用户是否尝试与该应用程序进行交互,并且您的应用程序将变得无响应。 这就是我们转向多线程的地方。

假设我们使用一个非常简单的数据结构来存储我们收到的数据样本,即一个普通的整数数组。 我们可能很想创建一个队列并像这样使用它:

看起来不错吧? 现在,我们正在后台线程上进行所有数据处理,而主线程仅用于更新视觉效果。 但是,这势必会崩溃。 但为什么? 答案有点技术性,但是考虑这一点很重要。

由于我们的队列是并发的,因此它将在线程上抛出要并行执行的工作项。 我们还将数组用作数据存储。 Swift数组是一种结构类型,这意味着它是一个值类型。 当您尝试将值附加到这样的数组时,您将:

  1. 分配一个新数组并复制旧数组中的值
  2. 追加新数据
  3. 将新引用写回您的变量
  4. 系统继续释放旧阵列使用的内存

想想如果两个线程将相同的数组复制到它们,然后将自己的数据附加到副本,然后将新的引用写回到我们的变量(一个在另一个之前,或者两个在同一时间)会发生什么。 第一种情况会给我们不正确的数据,因为首先写入的线程所写的数组中会丢失首先写入的线程中的数据。 第二种情况将导致我们的应用程序崩溃,因为两个线程无法同时获得对分配的内存的写访问权限。

考虑到这一点,我们可以使用DispatchQueue类附带的非常聪明的构造,即标志。 现在,我们可以像这样更改代码:

这看起来令人生畏,但我将解释它的作用。
每当添加一个将通过写入而更改数据结构的项目时,通过使用.barrier标志,我们告诉队列该特定工作项将需要自己执行。 这意味着队列将需要等待所有正在运行的线程完成,然后运行该项目并等待其完成,然后它才能再次开始并行执行代码。

当主线程需要访问数据以更新我们的视图时,它需要通过同步调用来遍历数据队列。 如果不这样做,则存在我们编写线程之一随时损坏其正在读取的数据的风​​险。

希望您能成功并从中获得一些新知识。 几天后重新阅读一遍可能会有所帮助,让自己有机会进行反思。

如果您有任何疑问,请随时发表评论,然后继续获取有关未来文章的通知。


要了解有关iOS开发的更多信息,可以查看我以前的文章:

iOS开发和错误的MVC类型

作为iOS开发人员开始时,您会听到很多有关MVC(模型-视图-控制器)模式及其状态的信息。

medium.com