WWDC21-Swif5.5特性早知道(并发) 一、前言 二、并发 三、Actors

一、前言

        WWDC21上,苹果宣布了Swift5.5新特性——并发!从此在语言层面而非库的层面完成了对并发(异步与并行)的支持。这是一个大喜讯!
        在早期进行多线程编发时,是十分困难的,需要手动处理线程的创建、销毁、线程间的同步。在iOS编程领域,苹果很早就引入了NSThread、GCD、NSOperation,这大幅降低了多线程编程的难度。但是,这些都是通过系统库来实现的,没有语法层面的支持,还是比较容易引起问题的。日常碰到的疑难问题大多数都与多线程相关,特别是多线程引起的内存问题,就更难处理。
        幸运的是——Swift5.5在语法层面支持了并发(多线程)的编程。这是一个巨大的进步!因为可以通过编译器的检查帮助我们规避由于我们疏忽、对并发的不了解等原因导致的问题。

二、并发

2.1 定义和调用异步函数

// 在函数、方法的右小括号后面添加关键字async,则标明此函数是一个异步函数(可能会被吊起、恢复)
func listPhotos(inGallery name: String) async -> [String] {
    let result = // ... some asynchronous networking code ...
    return result
}

// 在调用并发函数的时候需要添加关键字await
// 就和调用可能抛出异常的函数时添加try关键字相同
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[1]
let photo = await downloadPhoto(named: name)
show(photo)

        异步函数或方法不是在任何地方都可以调用的,只有在以下几个场景方可调用:

  • 在异步的函数、方法、属性内部可以调用衣服函数或方法
  • 在一个分离的子任务之中
  • 被@main标识的结构体、类、枚举所定义的静态main方法之中

2.2 异步序列

// 在此for循环之中,每次获得下一个元素时都是异步的
let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}

// 自定义的序列类型只要符合AsyncSequence协议就可以使用异步方法

2.3 并行调用异步方法

// 异步执行此方法,当前代码被挂起
let firstPhoto = await downloadPhoto(named: photoNames[0])
// 异步执行此方法,当前代码被挂起
let secondPhoto = await downloadPhoto(named: photoNames[1])
// 异步执行此方法,当前代码被挂起
let thirdPhoto = await downloadPhoto(named: photoNames[2])

let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

// ----------------------------------------

async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

// 一直运行到这里才挂起代码,上面三行代码可能在三个核心上同时执行
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

2.4 Task与Tash Group

async let firstPhoto = downloadPhoto(named: photoNames[0]) // 隐式创建了一个task
async let secondPhoto = downloadPhoto(named: photoNames[1]) // 隐式创建了一个task
async let thirdPhoto = downloadPhoto(named: photoNames[2]) // 隐式创建了一个task

// 所有的异步代码都属于某个task
// 通过明确创建Task Group可以管理多个task之间的关系
// Task 与 Task Group,就如同为对GCD进行封装的NSOperation

await withTaskGroup(of: Data.self) { taskGroup in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
    for name in photoNames {
        taskGroup.async { await downloadPhoto(named: name) }
    }
}

// 有明确的父子关系的task就属于结构化的
// 没有明确父子关系的task就属于非结构化的,其灵活性更大,但是开发者承担更多的责任

三、Actors

        简单理解actor 就是满足并发要求的class!
        简单理解,就是对actor属性的访问,默认加锁了的。
        actor是引用类型。

actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}

let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max) // 需要添加await标识

// 以下代码属于actor的一部分,所以不需要添加await
extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement > max {
            max = measurement
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章