Concurrency By Turorials (3) - 队列与线程

291 阅读4分钟

线程

线程实际上是执行线程的缩写,它表示正在运行的进程如何跨系统上的资源分割任务。 你的iOS应用程序是一个利用多个线程运行多个任务的进程。

在设备的CPU中,一次执行的线程数可以与核心线程数相同。

把你的应用分成多个线程有很多好处:

•执行速度更快:通过在多个线程上运行任务,可以完成工作,这将使它比串行运行一切更快地完成。

•响应性:如果你只在主UI线程上执行用户可见的工作,那么用户不会注意到应用程序变慢或者卡主,因为其他任务可以在另一个线程上执行。

•优化的资源消耗:操作系统高度优化了线程。

不会需要显式地创建线程。操作系统将使用更高的抽象为您处理所有线程创建。

Apple提供了线程管理所需的api,但是如果您试图自己直接管理它们,实际上可能会降低而不是提高性能。

操作系统跟踪许多统计数据,以了解何时应该分配或不应该销毁线程。

Dispatch queues

处理线程的方式是创建一个DispatchQueue。 创建DispatchQueue时,操作系统可能会创建一个或多个线程并将其分配给队列。 如果现有线程可用,则可以重用它们;如果没有,操作系统将根据需要创建它们。

Main queue

当应用程序启动时,将自动为创建一个主调度队列。它是一个负责UI的串行队列。 因为它被频繁地使用,Apple已经将它作为一个类变量提供,您可以通过DispatchQueue.main访问它。 除非与实际的UI工作相关,否则永远不要对主队列执行同步操作。 否则,您将锁定您的UI,这可能会降低您的应用程序性能。

Quality of service

当使用并发调度队列时,您需要告诉iOS任务有多重要,以便它能够将需要完成的工作与其他需要资源的任务进行适当的优先级排序

。请记住,高优先级的工作必须执行得更快,这可能需要更多的系统资源来完成,并且比低优先级的工作需要更多的能量。

如果你只是需要一个并发队列,但不想管理自己的队列,你可以使用DispatchQueue.global。

一共有六种

  1. .userInteractive
  2. .userInitiated
  3. .utility
  4. .background
  5. .default
  6. .unspecified

Self持有

DispatchQueue.global(qos: .utility).async { [weak self] in guard let self = self else { return } 
// Perform your work here // … 
// Switch back to the main queue to // update your UI DispatchQueue.main.async { 
self.textLabel.text = “New articles available!” } 
} 

DispatchQueue没有什么特别之处,它使闭包规则无效。如果您计划使用闭包捕获的变量(如self),则仍然需要确保正确地处理它们。

在GCD异步闭包中强捕获self不会导致引用循环(例如retain循环),因为一旦完成整个闭包将被释放,但它将延长self的生命周期。

如果你从一个视图控制器发出一个网络请求,这个请求在此期间已经被驳回,闭包仍然会被调用。

如果你弱捕获视图控制器,它将是nil。但是,如果您强捕获它,视图控制器将一直保持活动状态,直到闭包完成其工作。

DispatchWorkItem

一般都是通过闭包的方式在DispatchQueue传入需要做的任务,还有一种方式是通过DispatchWorkItem。 DispatchWorkItem是一个类,它提供了一个实际的对象来保存您希望提交给队列的代码。

比如:

let queue = DispatchQueue(label: “xyz”) queue.async { 
  print(“The block of code ran!”)
}

改为:

let queue = DispatchQueue(label: “xyz”)
let workItem = DispatchWorkItem {
  print(“The block of code ran!”)
}
queue.async(execute: workItem) 

取消DispatchWorkItem

您可能希望使用显式DispatchWorkItem的一个原因是,如果您需要在执行之前或执行期间取消任务。

如果您对DispatchWorkItem调用cancel(),将执行以下两个操作之一:

  1. 如果任务尚未在队列上启动,则将删除它。

  2. 如果任务当前正在执行,iscancel属性将被设置为true。

需要在代码中定期检查iscancel属性,并在可能的情况下采取适当的操作来取消任务。

贫瘠的依赖关系

let queue = DispatchQueue(label: “xyz”)
let backgroundWorkItem = DispatchWorkItem { }
let updateUIWorkItem = DispatchWorkItem { }
backgroundWorkItem.notify(queue: DispatchQueue.main, execute: updateUIWorkItem) 
queue.async(execute: backgroundWorkItem) 

DispatchWorkItem类还提供了一个notify(queue:execute:)方法,该方法可用于标识应该在当前工作项完成后执行的另一个DispatchWorkItem。

请注意,在指定要执行的后续工作项时,必须显式指定工作项应该针对哪个队列执行。