協(xié)程怎么理解
- 一種在程序中處理并發(fā)任務的方案;也是該方案的一個組件
- 協(xié)程和線程屬于一個層級的概念
- 協(xié)程中不存在線程,也不存在并行(并行不是并發(fā))
協(xié)程的好處
- 處理耗時任務,這種任務時常會堵塞主線程
- 保證主線程安全,即確保安全地從主線程調用任何suspend函數(shù)
- 協(xié)程讓異步邏輯同步化,杜絕回調地獄
- 協(xié)程最核心的點就是,函數(shù)或者一段程序能夠掛起,稍后再在掛起的位置恢復
下面是關于協(xié)程這個概念的一些描述
協(xié)程的開發(fā)人員 Roman Elizarov 是這樣描述協(xié)程的:協(xié)程就像非常輕量級的線程。線程是由系統(tǒng)調度
的,線程切換或線程阻塞的開銷都比較大。而協(xié)程依賴于線程,但是協(xié)程掛起時不需要阻塞線程,幾乎是無
代價的,協(xié)程是由開發(fā)者控制的。所以協(xié)程也像用戶態(tài)的線程,非常輕量級,一個線程中可以創(chuàng)建任意個協(xié)
程。
Coroutine,翻譯成”協(xié)程“,初始碰到的人馬上就會跟進程和線程兩個概念聯(lián)系起來。直接先說區(qū)別,
Coroutine是編譯器級的,Process和Thread是操作系統(tǒng)級的。Coroutine的實現(xiàn),通常是對某個語言
做相應的提議,然后通過后成編譯器標準,然后編譯器廠商來實現(xiàn)該機制。Process和Thread看起來也在
語言層次,但是內生原理卻是操作系統(tǒng)先有這個東西,然后通過一定的API暴露給用戶使用,兩者在這里有
不同。Process和Thread是os通過調度算法,保存當前的上下文,然后從上次暫停的地方再次開始計算,
重新開始的地方不可預期,每次CPU計算的指令數(shù)量和代碼跑過的CPU時間是相關的,跑到os分配的cpu時
間到達后就會被os強制掛起。Coroutine是編譯器的魔術,通過插入相關的代碼使得代碼段能夠實現(xiàn)分段
式的執(zhí)行,重新開始的地方是yield關鍵字指定的,一次一定會跑到一個yield對應的地方
對于多線程應用,CPU通過切片的方式來切換線程間的執(zhí)行,線程切換時需要耗時(保存狀態(tài),下次繼
續(xù))。協(xié)程,則只使用一個線程,在一個線程中規(guī)定某個代碼塊執(zhí)行順序。協(xié)程能保留上一次調用時的狀
態(tài),不需要像線程一樣用回調函數(shù),所以性能上會有提升。缺點是本質是個單線程,不能利用到單個CPU的
多個核
扔物線表述
對某些語言,比如Kotlin,這樣說是沒有問題的,Kotlin的協(xié)程庫可以指定協(xié)程運行的線程池,我們只
需要操作協(xié)程,必要的線程切換操作交給庫,從這個角度來說,協(xié)程就是一個線程框架。
但理論上我們可以在單線程語言如JavaScript、Python上實現(xiàn)協(xié)程(事實上他們已經實現(xiàn)了協(xié)程),這時
我們再叫它線程框架可能就不合適了。
協(xié)程依賴
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0")
啟動
1.協(xié)程需要運行在有協(xié)程上下文環(huán)境,在非協(xié)程環(huán)境憑空啟動協(xié)程,有三種方式
- GlobalScope.launch{}
在應用范圍內啟動一個協(xié)程,協(xié)程的生命周期與應用程序一致。這樣啟動的協(xié)程不能使線程?;睿拖袷刈o線程
由于這樣啟動的協(xié)程在啟動協(xié)程的組件已被銷毀但協(xié)程還存在的情況,可能導致資源耗盡,因此不推薦這樣啟動,尤其是在客戶端這種需要頻繁創(chuàng)建銷毀組件的場景
- 實現(xiàn)CoroutineScope + launch{}
這是在應用場景最推薦使用協(xié)程的方式,為自己的組件實現(xiàn)CoroutieScope接口,在需要的地方使用launch{}方法啟動協(xié)程。使得協(xié)程和該組件生命周期綁定,組件銷毀時,協(xié)程一并銷毀。從而實現(xiàn)安全可靠的協(xié)程調用
- runBlocking{}
啟動一個新協(xié)程,并阻塞當前線程,直到內部邏輯以及子線程邏輯全部執(zhí)行完成。
該方法的設計目的是讓suspend在編寫庫中能夠再常規(guī)堵塞代碼中使用,常在main方法和測試中使用
2.在一個協(xié)程中啟動子協(xié)程,一般來說有兩種
- launch{}
異步啟動一個字寫成
- async{}
異步啟動一個子協(xié)程,并返回Deffer對象,可通過調用Deffer.await()方法等待該子協(xié)程執(zhí)行完成并獲取結果,常用于并發(fā)-同步等待的情況
協(xié)程的掛起與恢復
- 常規(guī)的函數(shù)基礎操作包括,invoke(或Call)和return,協(xié)程新增了suspend和resume
1.suspend 也稱為掛起或者暫停,用于暫停執(zhí)行當前協(xié)程,并保存所有局部變量(掛起點保存了)
2.resume 用于讓已暫停的協(xié)程從其暫停處繼續(xù)執(zhí)行
- 掛起函數(shù)
1.使用suspend關鍵字修飾的函數(shù)叫做掛起函數(shù)
2.掛起函數(shù)只能在協(xié)程體內或者其他掛起函數(shù)內調用
調度器
- 所有的協(xié)程都必須在調度器中運行,即使他們在主線程上也是如此

QQ20211214-161133@2x.png
如果協(xié)程沒有指定調度器的話,默認在dispatchers.Default運行
任務泄露
- 當某個協(xié)程任務丟失,無法追蹤,導致內存、CPU、磁盤等資源浪費,甚至發(fā)送一個無用的網(wǎng)絡請求,這種情況叫做任務泄露
- 為了能夠避免協(xié)程泄露,Kotlin引入了結構化并發(fā)機制
結構化并發(fā)
- 取消任務,當某項任務不再需要時取消它
- 追蹤任務,當任務正在執(zhí)行時,追蹤它
- 發(fā)出錯誤信號,當協(xié)程失敗時,發(fā)出錯誤信號表明有錯誤發(fā)生

20211214-162408@2x.png