1.簡單介紹一下協(xié)程的前世今生
協(xié)程(英語:coroutine)馬爾文·康威于1958年發(fā)明了術(shù)語“coroutine”并用于構(gòu)建匯編程序 ,關(guān)于協(xié)程最初的出版解說在1963年發(fā)表。
協(xié)程的概念在60年代就已經(jīng)提出,目前在服務(wù)端中應(yīng)用比較廣泛,在高并發(fā)場景下使用極其合適,可以極大降低單機(jī)的線程數(shù),提升單機(jī)的連接和處理能力,直到近些年隨著前端扮演的角色越來越重要,業(yè)務(wù)越來越復(fù)雜,越來越多的回調(diào)地獄,讓前端代碼的可讀性越來越差,所以協(xié)程才逐漸的展露頭腳,比如在WWDC21 推出了Swift 5.5 支持異步編程 \ kotlin\ 以及ES7引入了 async/await。
2、協(xié)程的基本概念
協(xié)程是一種用戶態(tài)的輕量級線程,協(xié)程的調(diào)度完全由用戶控制。從技術(shù)的角度來說,“協(xié)程就是你可以暫停執(zhí)行的函數(shù)”。協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧,直接操作棧則基本沒有內(nèi)核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非???。
協(xié)程非常類似于線程。但是協(xié)程是協(xié)作式多任務(wù)的,而線程典型是搶占式多任務(wù)的。這意味著協(xié)程提供并發(fā)性而非并行性。

3、演示進(jìn)程和線程的買票代碼,進(jìn)行性能差異對比。帶著問題去思考什么是線程的并發(fā),什么是協(xié)程的并發(fā)?
非搶占式:
無需系統(tǒng)調(diào)用
作為對比,多線程執(zhí)行一般需要通過系統(tǒng)調(diào)用去分配線程進(jìn)行執(zhí)行
協(xié)程是線程安全的,無需鎖
掛起執(zhí)行時(shí):
保存寄存器和棧
不影響其他協(xié)程執(zhí)行
恢復(fù)執(zhí)行:
恢復(fù)之前的寄存器和棧
無縫切換回之前的執(zhí)行邏輯
協(xié)程的優(yōu)點(diǎn):
(1)無需線程上下文切換的開銷,協(xié)程避免了無意義的調(diào)度,由此可以提高性能(但也因此,程序員必須自己承擔(dān)調(diào)度的責(zé)
任,同時(shí),協(xié)程也失去了標(biāo)準(zhǔn)線程使用多CPU的能力)
(2)無需原子操作鎖定及同步的開銷
(3)方便切換控制流,簡化編程模型
(4)高并發(fā)+高擴(kuò)展性+低成本:一個(gè)CPU支持上萬的協(xié)程都不是問題。所以很適合用于高并發(fā)處理。
協(xié)程的缺點(diǎn):
(進(jìn)行阻塞(Blocking)操作(如IO時(shí))會(huì)阻塞掉整個(gè)程序。
一、GCD
a、線程之間的切換消耗

真正干活的不是線程,而是CPU。線程越多,干活不一定越快。
b、鎖的消耗
線程的管理,線程的管理也是資源的管理,因?yàn)槎鄠€(gè)線程會(huì)共享一個(gè)進(jìn)程內(nèi)的資源。

二、swift 協(xié)程
a、await\async
在 WWDC21 中 Swift 盼來了 async/await,作為現(xiàn)代編程語言的標(biāo)志之一,async/await 可以讓我們像編寫常規(guī)代碼一樣,輕松地編寫異步代碼,這樣能更直觀且更安全地表達(dá)我們的思路。async/await 是整個(gè) Swift 結(jié)構(gòu)化并發(fā)的基礎(chǔ)。
Task.init {
// 插入await關(guān)鍵字之后,調(diào)用異步函數(shù)work()
await self.work()
}
//在異步函數(shù)work()中,出發(fā)異步函數(shù) buy(),async 函數(shù)可以調(diào)用其他 async 函數(shù),也可以直接調(diào)用普通的同步函數(shù)
func work() async {
let start = CACurrentMediaTime()
for _ in 0 ..< ticketTotal {
let ticket = await self.buy()
print(ticket)
}
let end = CACurrentMediaTime()
print("協(xié)程-測量時(shí)間:\(end - start)")
}
//Continuation函數(shù)會(huì)等待異步回調(diào)結(jié)束。
func buy() async -> Int {
await withUnsafeContinuation { continuation in
self.asynBuyTicket { ticket in
continuation.resume(returning: ticket)
}
}
}
b、actor
Swift 5.5 中的并發(fā)模型旨在提供一個(gè)安全的編程模型,可以靜態(tài)檢測數(shù)據(jù)競爭和其他常見的并發(fā)錯(cuò)誤。結(jié)構(gòu)化并發(fā)為函數(shù)和閉包提供多線程競爭安全性保障。該模型適用于許多常見的設(shè)計(jì)模式,包括并行映射和并發(fā)回調(diào)模式等,但僅限于處理由閉包捕獲的狀態(tài)。
為此 Swift 5.5 引入了新的并發(fā)模型 Actor , 它通過數(shù)據(jù)隔離保護(hù)內(nèi)部的數(shù)據(jù),確保在給定時(shí)間只有一個(gè)線程可以訪問該數(shù)據(jù)。
狀態(tài)私有:外部無法直接去訪問 Actor 的內(nèi)部數(shù)據(jù)
只能接收和處理消息:外部想要與 Actor 通信只能發(fā)送消息
每個(gè) Actor一次只執(zhí)行一個(gè)消息:Actor 內(nèi)部在一個(gè)消息隊(duì)列中處理消息,保證了線程安全

actor Ticket {
var number = ticketTotal
var start = CACurrentMediaTime()
var end = CACurrentMediaTime()
func buy() {
if number == ticketTotal {
start = CACurrentMediaTime()
}
if number > 0 {
number -= 1
print((number))
}
if number == 0 {
end = CACurrentMediaTime()
print("actor-測量時(shí)間:\(end - start)")
}
}
}


actor 還支持異步屬性
struct BundleFile {
let filename: String
var contents: String {
get async throws {
guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else {
throw FileError.missing
}
do {
return try String(contentsOf: url)
} catch {
throw FileError.unreadable
}
}
}
}
c、Continuation
老業(yè)務(wù)怎么平滑的過渡到async呢?
Swift 5.5 提供了 Continuations 讓我們可以把已有的基于閉包的方法轉(zhuǎn)換成 async 方法。
let thread1 = Thread {
Task.init {
let image = try await AsynsData().processImageData()
self.imageView2.image = image
}
}
thread1.start()
let thread2 = Thread {
Task.init {
let image = try await AsynsData().processImageData()
self.imageView3.image = image
}
}
thread2.start()
func processImageData() async throws -> UIImage? {
let upload1 = try await uploadResource()
let upload2 = try await uploadResource()
let upload3 = try await uploadResource()
let downloadImage = try await downloadImage([upload1.data,
upload2.data,
upload3.data])
return downloadImage
}
//圖片下載
func downloadImage(_ data:[Data]) async throws -> UIImage {
await withUnsafeContinuation { continuation in
AF.download("https://httpbin.org/image/png").responseData { response in
if let data = response.value, let image = UIImage(data: data) {
continuation.resume(returning: image)
}
}
}
}
//文件上傳
func uploadResource() async throws -> (error: APISessionError?, data: Data) {
await withUnsafeContinuation { continuation in
guard let fileUrl = Bundle.main.url(forResource: "symbold", withExtension: "text") else {
continuation.resume(returning:(APISessionError.networkError(error: nil, statusCode: 503), Data()) )
return
}
AF.upload(fileUrl, to: "https://httpbin.org/post").response { response in
if let data = response.data {
continuation.resume(returning:(nil, data))
} else {
continuation.resume(returning:(.noData, Data()))
}
}
}
}