Kotlin实战指南十四:协程启动模式
轉載請標明出處:https://blog.csdn.net/zhaoyanjun6/article/details/96008400
本文出自【趙彥軍的博客】
文章目錄
- 協程啟動
- DEFAULT
- LAZY
- ATOMIC
- UNDISPATCHED
- 附錄
- 參考資料
協程啟動
說了這么多線程,原因嘛,畢竟大家對它是最熟悉的。協程的 API 設計其實也與之一脈相承,我們來看一段最簡單的啟動協程的方式:
GlobalScope.launch {//do what you want }那么這段代碼會怎么執行呢?我們說過,啟動協程需要三樣東西,分別是 上下文、啟動模式、協程體,協程體就好比 Thread.run 當中的代碼,自不必說。
本文將為大家詳細介紹 啟動模式。在 Kotlin 協程當中,啟動模式是一個枚舉:
public enum class CoroutineStart {DEFAULT,LAZY,@ExperimentalCoroutinesApiATOMIC,@ExperimentalCoroutinesApiUNDISPATCHED; }| DEFAULT | 立即執行協程體 |
| ATOMIC | 立即執行協程體,但在開始運行之前無法取消 |
| UNDISPATCHED | 立即在當前線程執行協程體,直到第一個 suspend 調用 |
| LAZY | 只有在需要的情況下運行 |
DEFAULT
四個啟動模式當中我們最常用的其實是 DEFAULT 和 LAZY。
DEFAULT是餓漢式啟動,launch 調用后,會立即進入待調度狀態,一旦調度器 OK 就可以開始執行。我們來看個簡單的例子:
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)GlobalScope.launch(start = CoroutineStart.DEFAULT) {log(1)delay(1000)log(3)}log(2)} }輸出結果是:
E/zhaoyanjun:: 2 main E/zhaoyanjun:: 1 DefaultDispatcher-worker-2 E/zhaoyanjun:: 3 DefaultDispatcher-worker-2LAZY
LAZY 是懶漢式啟動,launch后并不會有任何調度行為,協程體也自然不會進入執行狀態,直到我們需要它執行的時候。這其實就有點兒費解了,什么叫我們需要它執行的時候呢?就是需要它的運行結果的時候,launch調用后會返回一個 Job實例,對于這種情況,我們可以:
- 調用 Job.start,主動觸發協程的調度執行
- 調用 Job.join,隱式的觸發協程的調度執行
所以這個所謂的”需要“,其實是一個很有趣的措辭,后面你還會看到我們也可以通過 await來表達對 Deferred的需要。這個行為與 Thread.join不一樣,后者如果沒有啟動的話,調用 join 不會有任何作用
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val job = GlobalScope.launch(start = CoroutineStart.LAZY) {log(1)delay(1000)log(2)}log(3)job.start()log(4)} }執行結果:
E/zhaoyanjun:: 3 main E/zhaoyanjun:: 4 main E/zhaoyanjun:: 1 DefaultDispatcher-worker-1 E/zhaoyanjun:: 2 DefaultDispatcher-worker-1ATOMIC
ATOMIC 只有涉及 cancel 的時候才有意義,cancel本身也是一個值得詳細討論的話題,在這里我們就簡單認為 cancel 后協程會被取消掉,也就是不再執行了。那么調用cancel的時機不同,結果也是有差異的,例如協程調度之前、開始調度但尚未執行、已經開始執行、執行完畢等等。
為了搞清楚它與 DEFAULT 的區別,我們來看一段例子:
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val job = GlobalScope.launch(start = CoroutineStart.ATOMIC) {log(1)delay(1000)log(2)}log(3)job.cancel()log(4)} }執行結果:
E/zhaoyanjun:: 3 main E/zhaoyanjun:: 4 main E/zhaoyanjun:: 1 DefaultDispatcher-worker-1我們創建了協程后立即cancel,但由于是 ATOMIC模式,因此協程一定會被調度,因此 1、3、4 一定都會輸出。
從輸出結果看不出什么,現在我們用 DEFAULT 模式來執行一遍
執行結果:
E/zhaoyanjun:: 3 main E/zhaoyanjun:: 4 main對應的,如果是DEFAULT 模式,在第一次調度該協程時如果 cancel就已經調用,那么協程就會直接被 cancel 而不會有任何調用。
需要注意的是,cancel 調用一定會將該 job 的狀態置為 cancelling,只不過ATOMIC模式的協程在啟動時無視了這一狀態。
我們使用線程的時候,想要讓線程里面的任務停止執行也會面臨類似的問題,但遺憾的是線程中看上去與 cancel 相近的 stop 接口已經被廢棄,因為存在一些安全的問題。不過隨著我們不斷地深入探討,你就會發現協程的 cancel某種意義上更像線程的 interrupt。
UNDISPATCHED
有了前面的基礎,UNDISPATCHED 就很容易理解了。協程在這種模式下會直接開始在當前線程下執行,直到第一個掛起點,這聽起來有點兒像前面的 ATOMIC,不同之處在于 UNDISPATCHED 不經過任何調度器即開始執行協程體。當然遇到掛起點之后的執行就取決于掛起點本身的邏輯以及上下文當中的調度器了。
附錄
log 代碼
fun log(mes: Int) {Log.e("zhaoyanjun:", "$mes ${Thread.currentThread().name}")}參考資料
破解 Kotlin 協程(2) - 協程啟動篇 https://www.bennyhuo.com/2019/04/08/coroutines-start-mode/
個人微信號:zhaoyanjun125 , 歡迎關注
總結
以上是生活随笔為你收集整理的Kotlin实战指南十四:协程启动模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kotlin实战指南十三:协程
- 下一篇: Android Lifecycle 生命