目錄
kotlin之協(xié)程(一),線程,進程,協(xié)程,協(xié)程可以替換線程嗎?
kotlin之協(xié)程(二),Kotlin協(xié)程是什么睛竣、掛起是什么、掛起的非阻塞式
kotlin之協(xié)程(三),開始創(chuàng)建協(xié)程,launch,withContext
kotlin之協(xié)程(四),協(xié)程的核心關鍵字suspend
kotlin之協(xié)程(五),launch 函數(shù)以及協(xié)程的取消與超時
kotlin之協(xié)程(六),協(xié)程中的 async和launch的區(qū)別以及runBlocking
kotlin之協(xié)程(七),協(xié)程中relay、yield 區(qū)別
如果對于協(xié)程需要精細控制,比如說滋捶,一個用戶也許關閉了一個啟動了協(xié)程的界面次氨,那么現(xiàn)在協(xié)程的執(zhí)行結(jié)果已經(jīng)不再被需要了蠢沿,這時苛吱,它應該是可以被取消的
launch 函數(shù)
launch 函數(shù)定義
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
launch 是個擴展函數(shù)开瞭,接受3個參數(shù)夜焦,前面2個是常規(guī)參數(shù)壳澳,最后一個是個對象式函數(shù),這樣的話 kotlin 就可以使用以前說的閉包的寫法:() 里面寫常規(guī)參數(shù)茫经,{} 里面寫函數(shù)式對象的實現(xiàn)巷波,就像上面的例子一樣
GlobalScope.launch(Dispatchers.Unconfined) {...}
我們需要關心的是 launch 的3個參數(shù)和返回值 Job:
- CoroutineContext - 可以理解為協(xié)程的上下文萎津,在這里我們可以設置 CoroutineDispatcher 協(xié)程運行的線程調(diào)度器,有 4種線程模式:
- Dispatchers.Default
- Dispatchers.IO -
- Dispatchers.Main - 主線程
- Dispatchers.Unconfined - 沒指定抹镊,就是在當前線程
不寫的話就是 Dispatchers.Default 模式的锉屈,或者我們可以自己創(chuàng)建協(xié)程上下文,也就是線程池垮耳,newSingleThreadContext 單線程颈渊,newFixedThreadPoolContext 線程池
-
CoroutineStart - 啟動模式,默認是DEAFAULT终佛,也就是創(chuàng)建就啟動俊嗽;還有一個是LAZY,意思是等你需要它的時候铃彰,再調(diào)用啟動
- DEAFAULT - 模式模式绍豁,不寫就是默認
- ATOMIC -
- UNDISPATCHED
- LAZY - 懶加載模式,你需要它的時候牙捉,再調(diào)用啟動
block - 閉包方法體竹揍,定義協(xié)程內(nèi)需要執(zhí)行的操作
-
Job - 協(xié)程構(gòu)建函數(shù)的返回值,可以把 Job 看成協(xié)程對象本身邪铲,協(xié)程的操作方法都在 Job 身上了
- job.start() - 啟動協(xié)程芬位,除了 lazy 模式,協(xié)程都不需要手動啟動
- job.join() - 等待協(xié)程執(zhí)行完畢
- job.cancel() - 取消一個協(xié)程
- job.cancelAndJoin() - 等待協(xié)程執(zhí)行完畢然后再取消
創(chuàng)建一個協(xié)程
創(chuàng)建該 launch
函數(shù)返回了一個可以被用來取消運行中的協(xié)程的 Job
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
取消
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // 延遲一段時間
println("main: I'm tired of waiting!")
job.cancel() // 取消該作業(yè)
job.join() // 等待作業(yè)執(zhí)行結(jié)束
println("main: Now I can quit.")
程序執(zhí)行后的輸出如下:
job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
main: Now I can quit.
一旦 main 函數(shù)調(diào)用了 job.cancel带到,我們在其它的協(xié)程中就看不到任何輸出昧碉,因為它被取消了
超時
在實踐中絕大多數(shù)取消一個協(xié)程的理由是它有可能超時。 當你手動追蹤一個相關 Job的引用并啟動了一個單獨的協(xié)程在延遲后取消追蹤阴孟,這里已經(jīng)準備好使用 withTimeout 函數(shù)來做這件事晌纫。 來看看示例代碼:
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
運行后得到如下輸出:
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms
這里我們看到了TimeoutCancellationException異常,這異常是因為超時導致的異常取消
解決這個問題也很簡單通過withTimeout
的withTimeoutOrNull
函數(shù),代碼示例:
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // 在它運行得到結(jié)果之前取消它
}
println("Result is $result")
運行后得到如下輸出:
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Result is null
這樣就沒有拋出異常了