什么是并发?《Swift异步与并发编程》读书笔记(一
同步与异步
同步
异步
有意思的是,和字面意思相反地,同步函数返回、完成之前,运行它的线程无法执行其他操作。

UIKit和SwiftUI都不是线程安全的:对于用户输入的处理和UI的绘制,必须在与主线程绑定的 main runloop
中进行。为避免长耗时操作将主线程堵塞导致用户输入无响应、UI卡顿,最常见的应对办法是将同步操作转换成异步操作:开辟后台线程运行长耗时操作,然后在操作结束时提供运行在主线程的回调。

串行与并行
两者都是执行操作的顺序或方式
同步操作一定以串行方式执行。
串行:同一时刻的多个线程之间一定是为同一个操作使用,不会另作其他事,甚至只在同一个线程内逐条执行。
异步操作则主要以并行方式执行,线程间通信时会以串行方式执行,也就是既能并行也能串行。
并行:同一时刻的多个线程一并执行多个操作。
为降低理解并发的难度,提并发时,一般都限定在『』这个简化版的意义。

一般的结构化编程吸取了Goto式非结构化编程的经验,通过控制流,遵守通用的约定,建立编程结构。
而并发的发展历程也同上基本一致。
由于并发的耗时特征,在大型系统中,非结构化的并发编程在运行时跟踪单元操作、遭遇内存冲突时遇到的挑战更大,更难以解决,也就更迫切地需要一套编程理论来作为共识性的指导,也就有了结构化并发。
结构化并发
其可以概括为:
『即使进行并发操作,也要保证控制流路径的单一入口和单一出口。程序可以产生多个控制流来实现并发,但是所有的并发路径在出口时都应该处于完成 (或取消) 状态,并合并到一起。』
好处是:使抽象层得以有效运作。在这一结构下编码时要可以拍着胸脯保证代码不会跳转到结构外,是严格『自包含』的。
并发编程的困难大致上有两个:
如何确保顺序执行;————逻辑正确性
如何确保运算资源调度、访问不冲突。————内存安全性
异步函数
为了化解并发编程的两个困难,引入异步函数
func loadSignature() async throws -> String {
fatalError("暂未实现")
}
在返回箭头前加上 `async` 关键字就可以把函数声明为异步函数。
与 `throw` 关键字类似地,该关键字帮助编译器确保两件事:
它允许在函数体内部(正确地)使用 await 关键字;
它要求其他来源在调用这个函数时,使用 await 关键字。
await 代表了函数在此处可能会放弃当前线程,它是程序的潜在暂停点。
Task 任务
对于同步函数来说,线程决定了它的执行环境。
对于异步函数来说,任务决定了它的执行环境。
『Swift 并发编程中,结构化并发需要依赖异步函数,而异步函数又必须运行在某个任务上下文中,因此可以说,想要进行结构化并发,必须具有任务上下文。
实际上,Swift 结构化并发就是以任务为基本要素进行组织的。』
简单地使用 `Task.init` 就可以让我们获取一个任务执行的上下文环境,它接受 `async` 标记的闭包。而且能继承当前任务上下文的**优先级**等特性,创建一个新的任务树的根节点。我们可以在其中使用异步函数,用法类似于 `Task { 异步代码 }` 。
> Task的要点
> 创建、组织、检查和取消任务
> 一个任务具有它自己的优先级和取消标识,它可以拥有若干个子任务并在其中执行异步函数。
> 无论是正常完成还是抛出错误,子任务会将结果向上报告给父任务,在所有子任务完成之前 (不论是正常结束还是抛出),父任务是不会完成的。
> 当一个父任务被取消时,这个父任务的取消标识将被设置,并向下传递到所有的子任务中去。
Task Group 任务组
在任务运行上下文中,或具体来说——
在某个异步函数中,我们可以通过await withTaskGroup 为当前的任务添加一组结构化的并发子任务。
这些子任务在被添加后立刻开始执行,最终在离开group的作用域时再汇集到一起。
async let 异步绑定
类似于 `let` 地, `async let` 定义一条局部的不变变量,并通过等号右侧的表达式来初始化该不变变量。
不同的是,该初始化表达式必须是异步函数的调用,通过将其绑定到该不变变量上,在当前 `Task` 上下文创建一道并发执行的子任务并立刻加以执行。
`async let` 赋值之后将立刻执行,即使在 `await` 之前执行就已经完成,其结果依然可以等到 `await` 语句时再进行求值。
和 `Task.init` 新建一个任务根节点不同,`async let` 所创建的子任务是任务树上的叶子节点。
任务组和异步绑定的对比
`async let` 可以理解成任务组如 `withTaskGroup` 的语法糖,但是异步绑定并不能动态地表达任务的数量。
『一个大致的使用原则是,如果我们需要比较「严肃地」界定结构化并发的起始,那么用任务组 Task Group 的闭包将它限制起来,并发的结构会显得更加清晰;
而如果我们只是想要快速地并发开始少数几个任务,并减少其他模板代码的干扰,那么使用 async let 进行异步绑定,会让代码更简洁易读。』