什么是并發(fā)?《Swift異步與并發(fā)編程》讀書(shū)筆記(一
同步與異步
同步?
異步?
有意思的是,和字面意思相反地,同步函數(shù)返回、完成之前,運(yùn)行它的線(xiàn)程無(wú)法執(zhí)行其他操作。

UIKit和SwiftUI都不是線(xiàn)程安全的:對(duì)于用戶(hù)輸入的處理和UI的繪制,必須在與主線(xiàn)程綁定的?main runloop
?中進(jìn)行。為避免長(zhǎng)耗時(shí)操作將主線(xiàn)程堵塞導(dǎo)致用戶(hù)輸入無(wú)響應(yīng)、UI卡頓,最常見(jiàn)的應(yīng)對(duì)辦法是將同步操作轉(zhuǎn)換成異步操作:開(kāi)辟后臺(tái)線(xiàn)程運(yùn)行長(zhǎng)耗時(shí)操作,然后在操作結(jié)束時(shí)提供運(yùn)行在主線(xiàn)程的回調(diào)。

串行與并行
兩者都是執(zhí)行操作的順序或方式
同步操作一定以串行方式執(zhí)行。
????串行:同一時(shí)刻的多個(gè)線(xiàn)程之間一定是為同一個(gè)操作使用,不會(huì)另作其他事,甚至只在同一個(gè)線(xiàn)程內(nèi)逐條執(zhí)行。
異步操作則主要以并行方式執(zhí)行,線(xiàn)程間通信時(shí)會(huì)以串行方式執(zhí)行,也就是既能并行也能串行。
????并行:同一時(shí)刻的多個(gè)線(xiàn)程一并執(zhí)行多個(gè)操作。
為降低理解并發(fā)的難度,提并發(fā)時(shí),一般都限定在『』這個(gè)簡(jiǎn)化版的意義。

一般的結(jié)構(gòu)化編程吸取了Goto式非結(jié)構(gòu)化編程的經(jīng)驗(yàn),通過(guò)控制流,遵守通用的約定,建立編程結(jié)構(gòu)。
而并發(fā)的發(fā)展歷程也同上基本一致。
由于并發(fā)的耗時(shí)特征,在大型系統(tǒng)中,非結(jié)構(gòu)化的并發(fā)編程在運(yùn)行時(shí)跟蹤單元操作、遭遇內(nèi)存沖突時(shí)遇到的挑戰(zhàn)更大,更難以解決,也就更迫切地需要一套編程理論來(lái)作為共識(shí)性的指導(dǎo),也就有了結(jié)構(gòu)化并發(fā)。
結(jié)構(gòu)化并發(fā)
其可以概括為:
『即使進(jìn)行并發(fā)操作,也要保證控制流路徑的單一入口和單一出口。程序可以產(chǎn)生多個(gè)控制流來(lái)實(shí)現(xiàn)并發(fā),但是所有的并發(fā)路徑在出口時(shí)都應(yīng)該處于完成 (或取消) 狀態(tài),并合并到一起?!?/p>
好處是:使抽象層得以有效運(yùn)作。在這一結(jié)構(gòu)下編碼時(shí)要可以拍著胸脯保證代碼不會(huì)跳轉(zhuǎn)到結(jié)構(gòu)外,是嚴(yán)格『自包含』的。
并發(fā)編程的困難大致上有兩個(gè):
?如何確保順序執(zhí)行;————邏輯正確性
?如何確保運(yùn)算資源調(diào)度、訪(fǎng)問(wèn)不沖突?!獌?nèi)存安全性
異步函數(shù)
為了化解并發(fā)編程的兩個(gè)困難,引入異步函數(shù)
func loadSignature() async throws -> String {
????fatalError("暫未實(shí)現(xiàn)")
}
在返回箭頭前加上 `async` 關(guān)鍵字就可以把函數(shù)聲明為異步函數(shù)。
與 `throw` 關(guān)鍵字類(lèi)似地,該關(guān)鍵字幫助編譯器確保兩件事:
?它允許在函數(shù)體內(nèi)部(正確地)使用 await?關(guān)鍵字;
?它要求其他來(lái)源在調(diào)用這個(gè)函數(shù)時(shí),使用 await 關(guān)鍵字。
await?代表了函數(shù)在此處可能會(huì)放棄當(dāng)前線(xiàn)程,它是程序的潛在暫停點(diǎn)。
Task 任務(wù)
對(duì)于同步函數(shù)來(lái)說(shuō),線(xiàn)程決定了它的執(zhí)行環(huán)境。
對(duì)于異步函數(shù)來(lái)說(shuō),任務(wù)決定了它的執(zhí)行環(huán)境。
『Swift 并發(fā)編程中,結(jié)構(gòu)化并發(fā)需要依賴(lài)異步函數(shù),而異步函數(shù)又必須運(yùn)行在某個(gè)任務(wù)上下文中,因此可以說(shuō),想要進(jìn)行結(jié)構(gòu)化并發(fā),必須具有任務(wù)上下文。
實(shí)際上,Swift 結(jié)構(gòu)化并發(fā)就是以任務(wù)為基本要素進(jìn)行組織的?!?/p>
簡(jiǎn)單地使用 `Task.init` 就可以讓我們獲取一個(gè)任務(wù)執(zhí)行的上下文環(huán)境,它接受 `async` 標(biāo)記的閉包。而且能繼承當(dāng)前任務(wù)上下文的**優(yōu)先級(jí)**等特性,創(chuàng)建一個(gè)新的任務(wù)樹(shù)的根節(jié)點(diǎn)。我們可以在其中使用異步函數(shù),用法類(lèi)似于 `Task { 異步代碼 }` 。
> Task的要點(diǎn)
> 創(chuàng)建、組織、檢查和取消任務(wù)
> 一個(gè)任務(wù)具有它自己的優(yōu)先級(jí)和取消標(biāo)識(shí),它可以擁有若干個(gè)子任務(wù)并在其中執(zhí)行異步函數(shù)。
>?無(wú)論是正常完成還是拋出錯(cuò)誤,子任務(wù)會(huì)將結(jié)果向上報(bào)告給父任務(wù),在所有子任務(wù)完成之前 (不論是正常結(jié)束還是拋出),父任務(wù)是不會(huì)完成的。
>?當(dāng)一個(gè)父任務(wù)被取消時(shí),這個(gè)父任務(wù)的取消標(biāo)識(shí)將被設(shè)置,并向下傳遞到所有的子任務(wù)中去。
Task Group 任務(wù)組
在任務(wù)運(yùn)行上下文中,或具體來(lái)說(shuō)——
在某個(gè)異步函數(shù)中,我們可以通過(guò)await withTaskGroup?為當(dāng)前的任務(wù)添加一組結(jié)構(gòu)化的并發(fā)子任務(wù)。
這些子任務(wù)在被添加后立刻開(kāi)始執(zhí)行,最終在離開(kāi)group的作用域時(shí)再匯集到一起。
async let 異步綁定
類(lèi)似于 `let` 地, `async let` 定義一條局部的不變變量,并通過(guò)等號(hào)右側(cè)的表達(dá)式來(lái)初始化該不變變量。
不同的是,該初始化表達(dá)式必須是異步函數(shù)的調(diào)用,通過(guò)將其綁定到該不變變量上,在當(dāng)前 `Task` 上下文創(chuàng)建一道并發(fā)執(zhí)行的子任務(wù)并立刻加以執(zhí)行。
`async let` 賦值之后將立刻執(zhí)行,即使在 `await` 之前執(zhí)行就已經(jīng)完成,其結(jié)果依然可以等到 `await` 語(yǔ)句時(shí)再進(jìn)行求值。
和 `Task.init` 新建一個(gè)任務(wù)根節(jié)點(diǎn)不同,`async let` 所創(chuàng)建的子任務(wù)是任務(wù)樹(shù)上的葉子節(jié)點(diǎn)。
任務(wù)組和異步綁定的對(duì)比
`async let` 可以理解成任務(wù)組如 `withTaskGroup` 的語(yǔ)法糖,但是異步綁定并不能動(dòng)態(tài)地表達(dá)任務(wù)的數(shù)量。
『一個(gè)大致的使用原則是,如果我們需要比較「嚴(yán)肅地」界定結(jié)構(gòu)化并發(fā)的起始,那么用任務(wù)組 Task Group 的閉包將它限制起來(lái),并發(fā)的結(jié)構(gòu)會(huì)顯得更加清晰;
而如果我們只是想要快速地并發(fā)開(kāi)始少數(shù)幾個(gè)任務(wù),并減少其他模板代碼的干擾,那么使用 async let 進(jìn)行異步綁定,會(huì)讓代碼更簡(jiǎn)潔易讀?!?/p>