在阅读了Concurrent和Serial队列,同步和异步之后,我想我对如何创建队列及其执行顺序有所了解 . 我的问题是,在我看过的任何教程中,没有一个实际告诉你许多用例 . 例如:
我有一个网络管理器使用URLSessions并序列化json向我的api发送请求 . 将它包装在 .utility
Queue或_2618027中是否有意义,或者我只是不将它包装在队列中 .
let task = LoginTask(username: username, password: password)
let networkQueue = DispatchQueue(label: "com.messenger.network",
qos: DispatchQoS.userInitiated)
networkQueue.async {
task.dataTask(in: dispatcher) { (user, httpCode, error) in
self.presenter?.loginUserResponse(user: user, httpCode: httpCode, error: error)
}
}
我的问题是:是否有任何指导我可以知道何时需要使用队列,因为我无法在任何地方找到这些信息 . 我意识到苹果提供了示例用法,但它非常模糊
1 回答
调度队列在很多用例中使用,因此很难枚举它们,但有两个非常常见的用例如下:
一个很好的例子是图像处理,这是一个众所周知的计算(和内存)密集型过程 . 因此,您将为图像操作创建一个队列,然后您将每个图像处理任务分派到该队列 . 您可能还会在完成主队列后调度UI更新(因为所有UI更新必须在主线程上进行) . 常见的模式是:
调度队列的优点在于它极大地简化了编写多线程代码,这是一种非常复杂的技术 .
问题在于,启动网络请求的示例已经在后台线程上运行请求,并且
URLSession
为您管理所有这些,因此使用队列没什么 Value .为了完全公开,在上面讨论的基本调度队列之上和之外直接使用GCD(例如调度组或调度源)或间接(例如操作队列)的各种不同工具令人惊讶:
调度组:有时您将启动一系列异步任务,并希望在完成所有任务后收到通知 . 您可以使用调度组(有关随机示例,请参阅https://stackoverflow.com/a/28101212/1271826) . 这使您无需跟踪所有这些任务自己完成的时间 .
Dispatch "apply"(现在称为
concurrentPerform
):有时当您运行一些大规模并行任务时,您希望使用尽可能多的线程 . 因此,concurrentPerform
允许您有效地并行执行for
循环,Apple已针对您的特定设备的核心和CPU数量进行了优化,同时在任何时候都没有充斥着过多的并发任务,耗尽了有限数量的工作线程 . 有关并行运行for
循环的示例,请参阅https://stackoverflow.com/a/39949292/1271826 .派遣来源:
例如,如果您有一些后台任务正在做大量工作而您想要根据进度更新UI,有时这些UI更新可能比UI可以处理它们更快 . 因此,您可以使用调度源(
DispatchSourceUserDataAdd
)将后台进程与UI更新分离 . 有关示例,请参见前面提到的https://stackoverflow.com/a/39949292/1271826 .传统上,
Timer
在主运行循环上运行 . 但有时你想在后台线程上运行它,但使用Timer
这样做很复杂 . 但是您可以使用DispatchSourceTimer
(GCD计时器)在主队列以外的队列上运行计时器 . 有关如何创建和使用调度计时器的示例,请参阅https://stackoverflow.com/a/38164203/1271826 . 调度计时器也可用于避免使用基于target
的Timer
对象轻松引入的一些强引用循环 .障碍:有时在使用并发队列时,您希望大多数事情同时运行,但对于其他事情,要相对于队列中的其他所有内容进行串行运行 . 屏障是一种说“将此任务添加到队列中,但确保它不会运行的方法”与该队列上的任何其他内容同时发生 . “
屏障的一个示例是读写器模式,其中从某些存储器资源读取可以与所有其他读取同时发生,但是任何写入都不能与队列中的任何其他内容同时发生 . 见https://stackoverflow.com/a/28784770/1271826或https://stackoverflow.com/a/45628393/1271826 .
信号量的一个常见应用是使固有异步任务以更同步的方式运行 .
这种方法的优点在于,在异步网络请求完成之前,调度的任务不会完成 . 因此,如果您需要发出一系列网络请求,但不能同时运行它们,信号量可以实现这一点 .
但是,信号量应该谨慎使用,因为它们本身效率低下(通常会阻塞一个线程等待另一个线程) . 另外,请确保你从主线程中的信号量永远不会
wait
(因为你在上面的例子中为什么我在等待networkQueue
,而不是主队列 . 所有这些都被说过,通常比技术更好信号量,但它有时是有用的 .操作队列:操作队列 Build 在GCD调度队列之上,但提供了一些有趣的优点,包括:
能够在自定义
Operation
子类中包装固有异步任务 . (这避免了我前面讨论过的信号量技术的缺点 . )调度队列通常在后台线程上运行固有同步任务时使用,但有时你想要管理一堆本身异步的任务 . 一个常见的例子是在Operation
子类中包装异步网络请求 .能够轻松控制并发度 . 调度队列可以是串行队列或并发队列,但设计控制机制比较麻烦,例如说"run the queued tasks concurrent with respect to each other, but no more than four at any given time."操作队列使用
maxConcurrentOperationCount
可以更轻松 . (有关示例,请参阅https://stackoverflow.com/a/27022598/1271826 . )能够在各种任务之间 Build 依赖关系(例如,您可能有一个用于下载图像的队列和另一个用于操作图像的队列) . 使用操作队列,您可以有一个用于下载图像的操作,另一个用于处理图像,您可以使后者依赖于前者的完成 .
还有很多其他GCD相关的应用程序和技术,但这些是我使用的一些频率 .