翻译:Swift 5编写并发编程,并发解决方案和异步Operation
说明
异步操作允许执行长时间运行的任务,而不必阻塞调用线程,直到执行完成为止。这是建立关注点分离的好方法,特别是与在操作之间创建依赖项结合使用时。
如果您不熟悉操作,建议您先阅读博客文章 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相关推荐
- python并发编程调优_Python并发编程-并发解决方案概述
Python并发编程-并发解决方案概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.并发和并行区别 1>.并行(parallel) 同时做某些事,可以互不干扰的同一个时 ...
- Python|并发编程|爬虫|单线程|多线程|异步I/O|360图片|Selenium及JavaScript|Scrapy框架|BOM 和 DOM 操作简介|语言基础50课:学习(12)
文章目录 系列目录 原项目地址 第37课:并发编程在爬虫中的应用 单线程版本 多线程版本 异步I/O版本 总结 第38课:抓取网页动态内容 Selenium 介绍 使用Selenium 加载页面 查找 ...
- lingo编程的主要方法_java并发编程 --并发问题的根源及主要解决方法
并发问题的根源在哪 首先,我们要知道并发要解决的是什么问题?并发要解决的是单进程情况下硬件资源无法充分利用的问题.而造成这一问题的主要原因是CPU-内存-磁盘三者之间速度差异实在太大.如果将CPU的速 ...
- Java并发编程-并发工具包java.util.concurrent使用指南
译序 本指南根据 Jakob Jenkov 最新博客翻译,请随时关注博客更新 本指南已做成中英文对照阅读版的 pdf 文档,有兴趣的朋友可以去 Java并发工具包java.util.concurren ...
- 并发编程 — 并发数据结构--转载
并发编程系列文章: 初解线程池:http://ray-yui.iteye.com/blog/2072463 详解线程池:http://ray-yui.iteye.com/b ...
- java并发编程并发容器_Java并发编程:同步容器
为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...
- 并发编程 | 并发工具类 - 序章
总览 本章节的思维导图如下所示: 引言 在并发编程的世界中,我们面临着各种复杂的问题,例如线程间的同步.数据一致性保证.任务的分配与执行等.幸运的是,Java提供了一套强大的并发工具类,帮助我们更好地 ...
- Java并发编程-并发工具包(java.util.concurrent)使用指南(全)
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
- [并发编程]并发编程第二篇:利用并发编程,实现计算大量数据的和
利用并发编程,实现计算大量数据的和 实现代码: package tj.pojo.generate.main;import java.util.ArrayList; import java.util.L ...
- Java并发编程—并发和并行、线程上下文
文章目录 并发和并行 并发和并行的区别 上下文切换 相关问题 为什么循环次数少的情况下,单线程快? 什么时候需要用多线程? 线程上下文切换消耗的时长? 用什么测试的线程上下文?面试回答下面的工具会加分 ...
最新文章
- python如何判断季度_从python中的datetime对象中查找年份和季度
- python编写姓名年龄_python小工具 - alert弹框输出姓名年龄、求和
- new操作符的作用是什么
- ICCV2021 | 如何高效视频定位?QMUL北大Adobe强强联手提出弱监督CRM,性能SOTA
- 为什么中国学生会越学越呆?
- 面试官:背了几道面试题就敢说熟悉Java源码?我们不招连源码都不会看的人|原力计划...
- python 列表去重(数组)的几种方法_python 列表去重(数组)的几种方法(转)
- 简单理解javascript中的原型对象,实现对之间共享属性和行为
- 2022,微博稳了吗?
- 自媒体人常用工具,你还不知道的快速保存无水印视频图片方法
- Linux下播放器开发-交叉编译Mplayer
- Unity3D学习——使用PUN写一个聊天功能
- 随机迷宫生成算法整理分析
- 均方距离计算公式_均方末端距的统计计算法.ppt
- 已知像素焦距与图片像素求毫米焦距
- Vue中的@blur和@focus事件
- FIELD-SYMBOLS 动态内表
- Python pandas 读取csv/txt数据文件 python读取csv/txt文件
- 刚进公司就把祖上十八代单传的代码优化了是什么体验?
- 松果时序数据库(PinusDB)最佳实践
热门文章
- JAVA构造方法与构造方法的执行原理简单理解(栈与堆)
- linux sleeping进程多_你知道Linux进程的睡眠和唤醒操作?
- html页面能直接用vuex吗,vuex(多用于不同页面之间的数据共用和修改)
- 无机金属专业里有计算机课吗,无机非金属材料工程专业课程有不少
- html 文本框 获取焦点事件,JS的文本输入框获得焦点与失去焦点的事件
- Controller中servletFileUpload.parseRequest(request)解析为空
- 剖析云计算和大数据落地后x86服务器的业务痛点
- CentOS 6.5 shell中su切换自动输入密码
- Linux inittab和oracle lsntctl 启动的问题解决办法
- 2018湘南学院计算机分数线,湘南学院录取分数线2021是多少分(附历年录取分数线)...