首發(fā)于公眾號(hào): DSGtalk1989
30.協(xié)程取消與超時(shí)
-
如何取消我們需要的取消
上文中出現(xiàn)了
cancel
方法來(lái)進(jìn)行取消的操作,但是過(guò)程中,我們的例子我怕會(huì)有誤解,我們直接在協(xié)程還沒(méi)有啟動(dòng)的時(shí)候取消了它,我們來(lái)看下是否可以啟動(dòng)了一會(huì)兒還可以暫停呢。這個(gè)和我們java中熟知的Thread
比較不一樣,首先Thread
在很久以前就不跟你玩cancel
了,其次Thread
只要起來(lái)了,就停不下來(lái)了。不像協(xié)程,起都沒(méi)起,還能夠取消。我們變一種取消的方式再來(lái)看看。
fun main() = runBlocking { val startTime = System.currentTimeMillis() val job = launch { var nextPrintTime = startTime var i = 0 while (i < 5) { // 一個(gè)執(zhí)行計(jì)算的循環(huán),只是為了占用 CPU // 每秒打印消息兩次 if (System.currentTimeMillis() >= nextPrintTime) { println("I'm sleeping ${i++} ...") nextPrintTime += 500L } } } println("runBlocking start") delay(10) job.cancel() println("delay end") }
我們只是
delay
了10ms,照理來(lái)說(shuō)cancel
應(yīng)該立馬讓線程停掉,但是依然把launch
的協(xié)程給完全跑完了。因?yàn)槲覀儾](méi)有在協(xié)程中去檢測(cè)現(xiàn)在是否協(xié)程還活著。這個(gè)跟Thread
中的interrupt
方法比較像,中斷過(guò)后線程的信號(hào)量會(huì)改變,我們需要時(shí)刻的去關(guān)注線程當(dāng)前是否被打斷了,并且時(shí)刻的捕捉InterruptException
。前文中之所以我們可以操作
cancel
是因?yàn)楹瘮?shù)delay
是支持取消的。This suspending function is cancellable.
所以一旦遇到如上的情況,我們就需要在遍歷循環(huán)或者是計(jì)算的過(guò)程中去判斷一下
isActive
這個(gè)字段,協(xié)程是否還活著。同時(shí)調(diào)用yield
方法也能實(shí)現(xiàn)。
-
finally語(yǔ)句關(guān)閉資源
針對(duì)上面的情況,一旦我們?nèi)∠藚f(xié)程,而我們希望協(xié)程中使用到的一些資源可以釋放掉,我們就使用
try finally
。val job = launch { try { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } finally { println("I'm running finally") } }
-
執(zhí)行無(wú)法取消的代碼
有時(shí)候我們不希望我們的部分代碼段可以被取消,無(wú)論是各種可以取消的掛起函數(shù),還是其他的計(jì)算函數(shù)。
val job = launch { repeat(1000) { i -> println("I'm sleeping $i ...") withContext(NonCancellable){ delay(500L) } } }
這樣一來(lái),
delay
就無(wú)法取消了。 -
超時(shí)異常
java中,我們通常需要去中斷一個(gè)線程的最主要的原因就是超時(shí)了,這邊一樣,在kotlin中,為我們提供了一個(gè)非常方便的方法
withTimeout
,一旦超過(guò)了規(guī)定的時(shí)間,就會(huì)自動(dòng)拋出TimeoutCancellationException
,這是一個(gè)CancellationException
的子類(lèi),所以他也是需要掛起函數(shù)支持被取消才行。我們也可以使用
withTimeoutOrNull
,這個(gè)函數(shù)一旦超過(guò)了指定的時(shí)間,會(huì)立馬返回一個(gè)null
,而不是拋出異常。withTimeout(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } val result = withTimeoutOrNull(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } "Done" // 協(xié)程會(huì)在輸出這個(gè)消息之前被取消 } println("Result is $result")
Kotlin學(xué)習(xí)筆記之 1 基礎(chǔ)語(yǔ)法
Kotlin學(xué)習(xí)筆記之 2 基本數(shù)據(jù)類(lèi)型
Kotlin學(xué)習(xí)筆記之 4 循環(huán)控制
Kotlin學(xué)習(xí)筆記之 5 類(lèi)和對(duì)象
Kotlin學(xué)習(xí)筆記之 8 擴(kuò)展
Kotlin學(xué)習(xí)筆記之 9 數(shù)據(jù)類(lèi)與密封類(lèi)
Kotlin學(xué)習(xí)筆記之 11 枚舉類(lèi)
Kotlin學(xué)習(xí)筆記之 12 對(duì)象表達(dá)式和對(duì)象聲明
Kotlin學(xué)習(xí)筆記之 13 基礎(chǔ)操作符run、with、let、also、apply
Kotlin學(xué)習(xí)筆記之 14 包與導(dǎo)入
Kotlin學(xué)習(xí)筆記之 15 伴生對(duì)象
Kotlin學(xué)習(xí)筆記之 18 函數(shù)
Kotlin學(xué)習(xí)筆記之 19 高階函數(shù)與 lambda 表達(dá)式
Kotlin學(xué)習(xí)筆記之 20 內(nèi)聯(lián)函數(shù)
Kotlin學(xué)習(xí)筆記之 21 解構(gòu)聲明
Kotlin學(xué)習(xí)筆記之 27 類(lèi)型別名
Kotlin學(xué)習(xí)筆記之 28 協(xié)程基礎(chǔ)
Kotlin學(xué)習(xí)筆記之 29 上下文與調(diào)度器
Kotlin學(xué)習(xí)筆記之 30 協(xié)程取消與超時(shí)
Kotlin學(xué)習(xí)筆記之 31 協(xié)程掛起函數(shù)的組合