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
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章