说明

异步操作允许执行长时间运行的任务,而不必阻塞调用线程,直到执行完成为止。这是建立关注点分离的好方法,特别是与在操作之间创建依赖项结合使用时。

如果您不熟悉操作,建议您先阅读博客文章 Swift中的Operations和OperationQueues入门。这篇文章可以帮助您入门并介绍基本知识。让我们开始研究异步操作,首先查看它们之间的区别及其同步的对立面。

异步与同步操作

看起来差别不大;实际上,它只是一个A,但实际差异要大得多。同步操作更容易设置和使用,但是只要异步操作不阻塞调用线程就无法运行。

异步操作可以发挥最大的作用,从而充分利用Swift中的操作。由于可以运行长时间运行的异步任务,因此可以将它们用于任何任务。创建关注点分离或将操作用作应用程序基础的核心逻辑。

综上所述,异步操作使您能够:

  • 运行长时间运行的任务
  • 从操作中调度到另一个队列
  • 手动开始操作,没有风险

我将在下一段中对最后一点做更多解释。

手动开始操作

同步和异步操作都可以手动启动。手动启动基本上可以归结为start()手动调用方法,而不是使用anOperationQueue来管理执行。

同步操作始终阻塞调用线程,直到操作完成。因此,它们不太适合手动启动操作。使用异步任务时,阻塞调用线程的风险不太重要,因为它有可能分派到另一个线程。

不建议手动启动

即使现在尝试手动启动异步任务可能很诱人,但也不建议这样做。通过使用an,OperationQueue您无需考虑执行多个操作时的执行顺序,并且可以从诸如优先任务等更多功能中受益。因此,建议始终通过将操作添加到中开始操作OperationQueue

创建异步操作

创建异步操作都始于创建自定义子类并覆盖isAsynchronous属性。

class AsyncOperation: Operation {override var isAsynchronous: Bool {return true}override func main() {/// Use a dispatch after to mimic the scenario of a long-running task.DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(1), execute: {print("Executing")})}
}

这还不足以使任务异步,因为在执行print语句后,任务仍然直接进入完成状态。通过执行以下代码段可以证明这一点:

let operation = AsyncOperation()
let queue = OperationQueue()
queue.addOperations([operation], waitUntilFinished: true)
print("Operations finished")// Prints:
// Operations finished
// Executing

换句话说,在异步任务仍在执行时,该任务已经标记为完成,这可能导致意外行为。我们需要自己开始管理状态,以使操作异步进行。

管理异步操作的状态

为了正确管理状态,我们需要使用多线程和KVO支持覆盖isFinishedandisExecuting属性。该isExecuting属性如下所示:

private var _isExecuting: Bool = false
override private(set) var isExecuting: Bool {get {return lockQueue.sync { () -> Bool inreturn _isExecuting}}set {willChangeValue(forKey: "isExecuting")lockQueue.sync(flags: [.barrier]) {_isExecuting = newValue}didChangeValue(forKey: "isExecuting")}
}

我们在仅同步访问的私有属性中跟踪执行状态。正如您在博客文章Concurrency in Swift中所了解的那样,您知道我们需要使用锁队列来进行线程安全的写和读访问。我们使用willChangeValue(forKey:)didChangeValue(forKey:)来添加KVO支持,以确保OperationQueue正确更新获取的内容。

我们还需要覆盖start()更新状态的方法。重要的是要注意,super.start()因为我们现在正在自己处理状态,所以您永远不要调用此方法。

最后,我们添加了一个finish()方法,该方法允许我们在异步任务完成后将状态设置为完成。

将所有这些加在一起,我们得到一个看起来像这样的子类:

class AsyncOperation: Operation {private let lockQueue = DispatchQueue(label: "com.swiftlee.asyncoperation", attributes: .concurrent)override var isAsynchronous: Bool {return true}private var _isExecuting: Bool = falseoverride private(set) var isExecuting: Bool {get {return lockQueue.sync { () -> Bool inreturn _isExecuting}}set {willChangeValue(forKey: "isExecuting")lockQueue.sync(flags: [.barrier]) {_isExecuting = newValue}didChangeValue(forKey: "isExecuting")}}private var _isFinished: Bool = falseoverride private(set) var isFinished: Bool {get {return lockQueue.sync { () -> Bool inreturn _isFinished}}set {willChangeValue(forKey: "isFinished")lockQueue.sync(flags: [.barrier]) {_isFinished = newValue}didChangeValue(forKey: "isFinished")}}override func start() {print("Starting")isFinished = falseisExecuting = truemain()}override func main() {/// Use a dispatch after to mimic the scenario of a long-running task.DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(1), execute: {print("Executing")self.finish()})}func finish() {isExecuting = falseisFinished = true}
}

为了确保我们的任务确实有效,我们将执行与之前相同的代码:

let operation = AsyncOperation()
let queue = OperationQueue()
queue.addOperations([operation], waitUntilFinished: true)
print("Operations finished")// Prints:
// Starting
// Executing
// Operations finished

这很棒,正是我们想要的!唯一缺少的是取消。

添加取消支持

由于操作可以随时取消,因此在开始执行时需要考虑到这一点。可能是在任务开始之前操作已被取消。

我们可以通过在start()方法内部简单地添加一个防护来做到这一点:

override func start() {print("Starting")guard !isCancelled else { return }isFinished = falseisExecuting = truemain()
}

尽管isFinishedandisExecuting属性此时包含正确的值,但是我们仍然需要根据文档进行更新:

具体来说,您必须更改 finished to YES 返回的值和executing to 返回的值 NO。即使开始执行操作之前取消了操作,也必须进行这些更改。

因此,我们finish()start()防护内部的方法中调用该方法,使最终方法如下所示:

override func start() {print("Starting")guard !isCancelled else {finish()return}isFinished = falseisExecuting = truemain()
}

利用异步任务

在为长时间运行的任务创建子类之后,是时候从中受益了。最终的异步操作类如下所示:

class AsyncOperation: Operation {private let lockQueue = DispatchQueue(label: "com.swiftlee.asyncoperation", attributes: .concurrent)override var isAsynchronous: Bool {return true}private var _isExecuting: Bool = falseoverride private(set) var isExecuting: Bool {get {return lockQueue.sync { () -> Bool inreturn _isExecuting}}set {willChangeValue(forKey: "isExecuting")lockQueue.sync(flags: [.barrier]) {_isExecuting = newValue}didChangeValue(forKey: "isExecuting")}}private var _isFinished: Bool = falseoverride private(set) var isFinished: Bool {get {return lockQueue.sync { () -> Bool inreturn _isFinished}}set {willChangeValue(forKey: "isFinished")lockQueue.sync(flags: [.barrier]) {_isFinished = newValue}didChangeValue(forKey: "isFinished")}}override func start() {print("Starting")guard !isCancelled else {finish()return}isFinished = falseisExecuting = truemain()}override func main() {fatalError("Subclasses must implement `main` without overriding super.")}func finish() {isExecuting = falseisFinished = true}
}

main()方法由子类执行时,我们将触发致命错误。

例如,您要上传带有的文件FileUploadOperation

final class FileUploadOperation: AsyncOperation {private let fileURL: URLprivate let targetUploadURL: URLprivate var uploadTask: URLSessionTask?init(fileURL: URL, targetUploadURL: URL) {self.fileURL = fileURLself.targetUploadURL = targetUploadURL}override func main() {uploadTask = URLSession.shared.uploadTask(with: URLRequest(url: targetUploadURL), fromFile: fileURL) { (data, response, error) in// Handle the response// ...// Call finishself.finish()}}override func cancel() {uploadTask?.cancel()super.cancel()}
}

请注意,我们正在保存数据任务,因此可以根据需要取消它。

这只是一个非常基本的例子。在 Collect by WeTransfer应用程序中,我们经常使用以下操作:

  • Content creation内容创作
  • Content receiving内容接收
  • Content uploading内容上传
  • Content enriching内容丰富

还有更多。很棒的事情是,您可以将这些操作链接在一起,如上一篇有关操作入门的文章所述。

结论

而已!我们创建了异步操作,您可以在项目中直接使用它。希望它可以使您以更好的性能将关注点与代码更好地分离。

这篇文章是系列文章的一部分:

  • Swift中的Operations和OperationQueues入门
  • 在Swift中编写并发解决方案的异步操作(本文)
  • 通过使用泛型进行高级异步操作

也可以以Swift Playground的形式找到:https://github.com/AvdLee/AsyncOperations

如果您想进一步提高Swift知识,请查看 Swift类别页面。随意 与我联系 或鸣叫我在 Twitter的 ,如果您有任何额外的提示或反馈。

谢谢!

参考

https://www.avanderlee.com/swift/asynchronous-operations/

翻译:Swift 5编写并发编程,并发解决方案和异步Operation相关推荐

  1. python并发编程调优_Python并发编程-并发解决方案概述

    Python并发编程-并发解决方案概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.并发和并行区别 1>.并行(parallel) 同时做某些事,可以互不干扰的同一个时 ...

  2. Python|并发编程|爬虫|单线程|多线程|异步I/O|360图片|Selenium及JavaScript|Scrapy框架|BOM 和 DOM 操作简介|语言基础50课:学习(12)

    文章目录 系列目录 原项目地址 第37课:并发编程在爬虫中的应用 单线程版本 多线程版本 异步I/O版本 总结 第38课:抓取网页动态内容 Selenium 介绍 使用Selenium 加载页面 查找 ...

  3. lingo编程的主要方法_java并发编程 --并发问题的根源及主要解决方法

    并发问题的根源在哪 首先,我们要知道并发要解决的是什么问题?并发要解决的是单进程情况下硬件资源无法充分利用的问题.而造成这一问题的主要原因是CPU-内存-磁盘三者之间速度差异实在太大.如果将CPU的速 ...

  4. Java并发编程-并发工具包java.util.concurrent使用指南

    译序 本指南根据 Jakob Jenkov 最新博客翻译,请随时关注博客更新 本指南已做成中英文对照阅读版的 pdf 文档,有兴趣的朋友可以去 Java并发工具包java.util.concurren ...

  5. 并发编程 — 并发数据结构--转载

    并发编程系列文章:        初解线程池:http://ray-yui.iteye.com/blog/2072463        详解线程池:http://ray-yui.iteye.com/b ...

  6. java并发编程并发容器_Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...

  7. 并发编程 | 并发工具类 - 序章

    总览 本章节的思维导图如下所示: 引言 在并发编程的世界中,我们面临着各种复杂的问题,例如线程间的同步.数据一致性保证.任务的分配与执行等.幸运的是,Java提供了一套强大的并发工具类,帮助我们更好地 ...

  8. Java并发编程-并发工具包(java.util.concurrent)使用指南(全)

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

  9. [并发编程]并发编程第二篇:利用并发编程,实现计算大量数据的和

    利用并发编程,实现计算大量数据的和 实现代码: package tj.pojo.generate.main;import java.util.ArrayList; import java.util.L ...

  10. Java并发编程—并发和并行、线程上下文

    文章目录 并发和并行 并发和并行的区别 上下文切换 相关问题 为什么循环次数少的情况下,单线程快? 什么时候需要用多线程? 线程上下文切换消耗的时长? 用什么测试的线程上下文?面试回答下面的工具会加分 ...

最新文章

  1. python如何判断季度_从python中的datetime对象中查找年份和季度
  2. python编写姓名年龄_python小工具 - alert弹框输出姓名年龄、求和
  3. new操作符的作用是什么
  4. ICCV2021 | 如何高效视频定位?QMUL北大Adobe强强联手提出弱监督CRM,性能SOTA
  5. 为什么中国学生会越学越呆?
  6. 面试官:背了几道面试题就敢说熟悉Java源码?我们不招连源码都不会看的人|原力计划...
  7. python 列表去重(数组)的几种方法_python 列表去重(数组)的几种方法(转)
  8. 简单理解javascript中的原型对象,实现对之间共享属性和行为
  9. 2022,微博稳了吗?
  10. 自媒体人常用工具,你还不知道的快速保存无水印视频图片方法
  11. Linux下播放器开发-交叉编译Mplayer
  12. Unity3D学习——使用PUN写一个聊天功能
  13. 随机迷宫生成算法整理分析
  14. 均方距离计算公式_均方末端距的统计计算法.ppt
  15. 已知像素焦距与图片像素求毫米焦距
  16. Vue中的@blur和@focus事件
  17. FIELD-SYMBOLS 动态内表
  18. Python pandas 读取csv/txt数据文件 python读取csv/txt文件
  19. 刚进公司就把祖上十八代单传的代码优化了是什么体验?
  20. 松果时序数据库(PinusDB)最佳实践

热门文章

  1. JAVA构造方法与构造方法的执行原理简单理解(栈与堆)
  2. linux sleeping进程多_你知道Linux进程的睡眠和唤醒操作?
  3. html页面能直接用vuex吗,vuex(多用于不同页面之间的数据共用和修改)
  4. 无机金属专业里有计算机课吗,无机非金属材料工程专业课程有不少
  5. html 文本框 获取焦点事件,JS的文本输入框获得焦点与失去焦点的事件
  6. Controller中servletFileUpload.parseRequest(request)解析为空
  7. 剖析云计算和大数据落地后x86服务器的业务痛点
  8. CentOS 6.5 shell中su切换自动输入密码
  9. Linux inittab和oracle lsntctl 启动的问题解决办法
  10. 2018湘南学院计算机分数线,湘南学院录取分数线2021是多少分(附历年录取分数线)...