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