- 取消作用域會(huì)取消它的子協(xié)程校翔。
- 被取消的子協(xié)程并不會(huì)影響其余兄弟協(xié)程踱稍。
- 協(xié)程通過拋出一個(gè)特殊的異常CancellationException來處理取消操作曲饱。
- 所有kotlinx.coroutines中的掛起函數(shù)(withContext、delay等)都是可取消的
取消作用域會(huì)取消它的子協(xié)程
val b = runBlocking<Unit> {
val scope = CoroutineScope(Dispatchers.Default)
scope.launch {
delay(1000)
println("job 1")
}
scope.launch {
delay(1000)
println("job 2")
}
delay(100)
scope.cancel()
}
當(dāng)scope協(xié)程作用被取消寞射,其作用域中的子協(xié)程都會(huì)被取消
"job 1" 和"job 2"將都不會(huì)被輸出
被取消的子協(xié)程并不會(huì)影響其余兄弟協(xié)程
val b = runBlocking<Unit> {
val scope = CoroutineScope(Dispatchers.Default)
val job1=scope.launch {
delay(1000)
println("job 1")
}
val job2=scope.launch {
delay(1000)
println("job 2")
}
delay(100)
job1.cancel()
}
輸出結(jié)果:
16455-16540 I/System.out: job 2
協(xié)程通過拋出一個(gè)特殊的異常CancellationException來處理取消操作
val b = runBlocking<Unit> {
val job1 = GlobalScope.launch {
try {
delay(1000)
println("job 1")
} catch (e: Exception) {
e.printStackTrace()
}
}
delay(100)
job1.cancel()//調(diào)用取消會(huì)拋出CancellationException 會(huì)被靜默處理渔工,使用try catch捕獲查看
job1.join()
// job1.cancelAndJoin() 等同上面兩行代碼
}
所有kotlinx.coroutines中的掛起函數(shù)(withContext锌钮、delay等)都是可取消的
CPU密集型任務(wù)取消
- isActive 是一個(gè)可以被使用在CoroutineScope中的擴(kuò)展屬性桥温,檢查Job是否處于活躍狀態(tài)
- ensureActive(),如果job處于非活躍狀態(tài),這個(gè)方法會(huì)立即執(zhí)行拋出異常
- yield函數(shù)會(huì)檢查所有協(xié)程的狀態(tài)梁丘,如果已經(jīng)取消侵浸,則拋出CancellationException予以相應(yīng)。此外氛谜,它還會(huì)嘗試讓出線程的執(zhí)行權(quán)掏觉,給其他協(xié)程提供執(zhí)行機(jī)會(huì)。
val b = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5 ) {
if (System.currentTimeMillis() >= nextPrintTime) {
println("job:I'm sleeping ${i++} .....")
nextPrintTime += 500
}
}
}
delay(1300)
println("main:I'm tired of waiting!")
job.cancelAndJoin()
println("main:Now I can quit.")
輸出內(nèi)容:
15527-15568 I/System.out: job:I'm sleeping 0 .....
15527-15568 I/System.out: job:I'm sleeping 1 .....
15527-15568 I/System.out: job:I'm sleeping 2 .....
15527-15527 I/System.out: main:I'm tired of waiting!
15527-15568 I/System.out: job:I'm sleeping 3 .....
15527-15568 I/System.out: job:I'm sleeping 4 .....
15527-15527 I/System.out: main:Now I can quit.
任務(wù)仍然執(zhí)行并沒有取消成功值漫,在CPU在做密集型計(jì)算任務(wù)時(shí)澳腹,是不允許直接取消,
可以通過job的生命周期屬性來判斷
val b = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5 && isActive) { //添加isActive job屬性判斷
if (System.currentTimeMillis() >= nextPrintTime) {
println("job:I'm sleeping ${i++} .....")
nextPrintTime += 500
}
}
}
delay(1300)
println("main:I'm tired of waiting!")
job.cancelAndJoin()
println("main:Now I can quit.")
}
輸出結(jié)果:
16211-16296 I/System.out: job:I'm sleeping 0 .....
16211-16296 I/System.out: job:I'm sleeping 1 .....
16211-16296 I/System.out: job:I'm sleeping 2 .....
16211-16211 I/System.out: main:I'm tired of waiting!
16211-16211 I/System.out: main:Now I can quit.
還可以使用ensureActive()來進(jìn)行判斷取消任務(wù)杨何,ensureActive()內(nèi)部實(shí)現(xiàn)同樣是使用isActive來判斷酱塔,但是ensureActive()會(huì)拋出CancellationException,會(huì)被靜默處理危虱,可以捕獲此異常
val b = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5 ) {
ensureActive()//會(huì)拋出CancellationException
if (System.currentTimeMillis() >= nextPrintTime) {
println("job:I'm sleeping ${i++} .....")
nextPrintTime += 500
}
}
}
delay(1300)
println("main:I'm tired of waiting!")
job.cancelAndJoin()
println("main:Now I can quit.")
}
yield函數(shù)會(huì)檢查所有協(xié)程的狀態(tài)羊娃,如果已經(jīng)取消,則拋出CancellationException予以相應(yīng)埃跷。此外蕊玷,它還會(huì)嘗試讓出線程的執(zhí)行權(quán)邮利,給其他協(xié)程提供執(zhí)行機(jī)會(huì)。
val b = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5 ) {
yield()//會(huì)拋出CancellationException
if (System.currentTimeMillis() >= nextPrintTime) {
println("job:I'm sleeping ${i++} .....")
nextPrintTime += 500
}
}
}
delay(1300)
println("main:I'm tired of waiting!")
job.cancelAndJoin()
println("main:Now I can quit.")
}
應(yīng)用場景計(jì)算太過密集垃帅,占用了大部分資源延届,使用yield()可以在取消后讓出執(zhí)行權(quán)給其他執(zhí)行
協(xié)程取消的副作用
- 在finally中釋放資源。
- use函數(shù):該函數(shù)只能被事項(xiàng)了Closeable的對(duì)象使用贸诚,程序結(jié)束的時(shí)候會(huì)自動(dòng)調(diào)用close方法祷愉,適合文件對(duì)象
val b = runBlocking<Unit> {
val job = launch(Dispatchers.Default) {
repeat(1000) { i ->
try {
println("job:I'm sleeping ${i} .....")
delay(500)
} finally {
println("job:I'm running finally!")
}
}
}
delay(1300)
println("main:I'm tired of waiting!")
job.cancelAndJoin()
println("main:Now I can quit.")
}
輸出結(jié)果:
22131-22231 I/System.out: job:I'm sleeping 0 .....
22131-22231 I/System.out: job:I'm running finally!
22131-22231 I/System.out: job:I'm sleeping 1 .....
22131-22231 I/System.out: job:I'm running finally!
22131-22231 I/System.out: job:I'm sleeping 2 .....
22131-22131 I/System.out: main:I'm tired of waiting!
22131-22231 I/System.out: job:I'm running finally!
22131-22131 I/System.out: main:Now I can quit.
use函數(shù):
val b = runBlocking<Unit> {
BufferedReader(FileReader("file:///android_asset/citys.json")).use{
var line: String?
while (true) {
line = readLine() ?: break
println(line)
}
}
}
不能被取消的任務(wù)
- 處于取消狀態(tài)的協(xié)程不能夠掛起(運(yùn)行不能取消的代碼),當(dāng)協(xié)程被取消后需要調(diào)用掛起函數(shù)赦颇,我們需要將清理的任務(wù)代碼放置于 NonCancellable CoroutineContext 中二鳄。
- 這樣會(huì)掛起運(yùn)行中的代碼,并保持協(xié)程的取消中狀態(tài)知道任務(wù)處理完成媒怯。
超時(shí)任務(wù)
- 很多情況下取消一個(gè)協(xié)程的理由是它有可能超時(shí)订讼。
- withTimeoutOrNull 通過返回null來進(jìn)行超時(shí)操作,從而替代拋出一個(gè)異常