zl程序教程

您现在的位置是:首页 >  其它

当前栏目

SwiftUI GCD基础教程

SwiftUI 基础教程 gcd
2023-09-11 14:18:48 时间

GCD是用于管理并发操作的低级API。通过将计算量大的任务推迟到后台,可以帮助您提高应用程序的响应速度。它比锁和线程更容易使用并发模型。

在iOS中,一个进程或应用程序由一个或多个线程组成。操作系统调度程序彼此独立地管理线程。每个线程可以并发执行,但是由系统决定是否发生这种情况,何时发生以及如何发生。

单核设备通过称为时间分片的方法实现并发。他们运行一个线程,执行上下文切换,然后运行另一个线程。

在这里插入图片描述

另一方面,多核设备通过并行性同时执行多个线程。

GCD建立在线程之上。它在后台管理一个共享线程池。使用GCD,您可以添加代码块或工作项来调度队列,而GCD决定在哪个线程上执行它们。

在构造代码时,您会发现可以同时运行的代码块以及不应运行的代码块。然后,您可以使用GCD来利用并发执行。

请注意,GCD根据系统和可用系统资源来决定需要多少并行性。重要的是要注意并行性要求并发,但是并发不能保证并行性。

基本上,并发与结构有关,而并行与执行有关。

Queue

如前所述,GCD 通过适当命名为的类在调度队列上进行操作DispatchQueue。您将工作单元提交到此队列,GCD将按FIFO顺序执行它们(先进先出),确保提交的第一个任务是第一个开始的任务。

调度队列是线程安全的,这意味着您可以同时从多个线程访问它们。当您了解调度队列如何为自己的部分代码提供线程安全时,GCD的好处显而易见。这里的关键是要选择合适的种类调度队列和右的调度功能提交您的作品到队列中。

队列可以是串行或并行。串行队列可确保在任何给定时间仅运行一项任务。GCD控制执行时间。您将不知道一个任务结束与下一个任务开始之间的时间:

在这里插入图片描述

并发队列允许多个任务同时运行。队列保证任务按照您添加任务的顺序开始。任务可以按任何顺序完成,并且您不知道下一个任务开始所花费的时间,也不知道在任何给定时间正在运行的任务数。

这是设计使然:您的代码不应依赖于这些实现细节。

请参阅下面的示例任务执行:

在这里插入图片描述

请注意任务1,任务2和任务3如何快速启动。另一方面,任务1在任务0之后需要一段时间才能开始。还请注意,虽然任务3在任务2之后开始,但它首先完成。

何时开始任务的决定完全取决于GCD。如果一个任务的执行时间与另一个任务的执行时间重叠,则由GCD决定它是否应该在另一个内核上运行(如果有一个内核可用),或者执行上下文切换以运行另一个任务。

GCD提供三种主要的队列类型:

主队列:在主线程上运行,是一个串行队列。
全局队列:整个系统共享的并发队列。有四个这样的队列,它们具有不同的优先级:高,默认,低和后台。后台优先级队列的优先级最低,并且在任何I / O活动中都会对其进行限制,以最大程度地减少负面的系统影响。
自定义队列:您创建的队列可以是串行或并发的。这些队列中的请求实际上最终在全局队列之一中结束。
将任务发送到全局并发队列时,您无需直接指定优先级。而是,您指定服务质量(QoS)类属性。这表明了任务的重要性,并指导GCD确定赋予任务的优先级。

QoS类为:

与用户互动:这表示必须立即完成的任务才能提供良好的用户体验。使用它进行UI更新,事件处理和要求低延迟的小型工作负载。在您的应用执行期间,此类中完成的总工作量应该很小。这应该在主线程上运行。
用户启动的:用户从UI启动这些异步任务。当用户等待即时结果以及继续用户交互所需的任务时,请使用它们。它们在高优先级全局队列中执行。
实用程序:这代表长时间运行的任务,通常带有用户可见的进度指示器。使用它进行计算,I / O,联网,连续数据馈送和类似任务。该类旨在提高能源效率。这将被映射到低优先级的全局队列中。
背景:这表示用户不直接知道的任务。使用它来进行预取,维护和其他不需要用户交互且对时间不敏感的任务。这将被映射到后台优先级全局队列中。

同步与异步

使用GCD,您可以同步或异步分派任务。

一个同步功能将控制返回到任务完成后调用者。您可以通过调用来同步安排工作单元DispatchQueue.sync(execute:)。

一个异步函数立即返回,订货任务的开始,但不等待它完成。因此,异步函数不会阻止当前执行线程继续进行下一个函数。您可以通过调用异步安排工作单元DispatchQueue.async(execute:)。

管理任务

您现在已经听说了很多任务。就本教程而言,您可以将任务视为闭包。闭包是可以存储和传递的自包含,可调用的代码块。

您提交给的每个任务DispatchQueue都是一个DispatchWorkItem。您可以配置DispatchWorkItem诸如QoS类之类的行为,或者是否生成新的分离线程。

处理后台任务

掌握了所有这些GCD知识之后,就该对您的第一个应用程序进行改进了!

返回该应用程序,然后从您的照片库添加一些照片,或使用Le Internet选项下载一些照片。点按照片。请注意,显示照片详细信息视图需要多长时间。在较慢的设备上观看大图像时,延迟更加明显。

重载视图控制器的操作viewDidLoad()很容易,导致视图出现之前需要等待很长时间。如果不是在加载时绝对必要的话,最好将工作卸载到后台。

这听起来像一个工作DispatchQueue的async!


通常,您需要async在后台执行基于网络或CPU密集型任务而又不阻塞当前线程时使用。

这是有关如何以及何时使用以下各种队列的快速指南async:

  • Main Queue主队列:这是在并发队列上完成任务中的工作后更新UI的常见选择。为此,您需要在另一个内编写一个闭包。以主队列为目标并进行调用可async确保此新任务将在当前方法完成后的某个时间执行。
  • Global Queue全局队列:这是在后台执行非UI工作的常见选择。
  • Custom Serial Queue 自定义串行队列:当您要串行执行后台工作并对其进行跟踪时,这是一个不错的选择。由于您一次只知道一个任务正在执行,因此消除了资源争用和争用条件。请注意,如果您需要方法中的数据,则必须声明另一个闭包来检索它,或考虑使用sync。

为什么不使用Timer?如果您有重复的任务,而这些任务更易于安排,则可以考虑使用它Timer。坚持使用调度队列的两个原因asyncAfter()。

一种是可读性。要使用Timer它,必须定义一个方法,然后使用选择器创建计时器或调用已定义的方法。使用DispatchQueue和asyncAfter(),您只需添加一个闭包即可。

Timer是在运行循环中安排的,因此您还必须确保将其安排在正确的运行循环中(在某些情况下,请选择正确的运行循环模式)。在这方面,使用调度队列更容易。

处理读者-作家问题

在Swift中,使用let关键字声明的任何变量都是常量,因此是只读且线程安全的。var但是,使用关键字声明该变量,并且变量变为可变的并且不是线程安全的,除非将数据类型设计为如此。声明为可变时,Swift集合类型喜欢Array和Dictionary不是线程安全的。

尽管许多线程可以Array同时读取可变的实例,而不会出现问题,但是让一个线程修改数组而另一个线程读取数组却并不安全。您的单身人士不会阻止这种情况在其当前状态下发生。