[Kotlin] Coroutine - 취소
by its_TIMI취소에 협조하는 방법
delay(), yield()같은 kotlinx.coroutines package 의 suspend function 사용.
코루틴이 취소가 되려면 대상 코루틴이 취소에 협조를 해주어야하는데, 그 취소에 협조해주는 방법에는
1. delay()를 사용하는 방법
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
printWithThread("START") // coroutine #1
val job1 = launch {
delay(1_000L)
printWithThread("job1") // coroutine #2
}
val job2 = launch {
delay(1_000L)
printWithThread("job2") // coroutine #3
}
delay(100)
job1.cancel()
}
[main @coroutine#1] START
[main @coroutine#3] job2
Process finished with exit code 0
하지만 주의할 점!
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
printWithThread("START") // coroutine #1
val job1 = launch {
delay(10)
printWithThread("job1") // coroutine #2
}
delay(100)
job1.cancel()
}
이렇게 호다닥 실행시켜버리면 취소 문장(?)이 실행되기도 전에 코루틴 끝나벌임
[main @coroutine#1] START
[main @coroutine#2] job1
Process finished with exit code 0
---
delay()를 안써버리면
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
val job = launch{
var i = 1
var nextPrintTime = System.currentTimeMillis()
while ( i <= 5 ){
if (nextPrintTime <= System.currentTimeMillis()){
printWithThread("${i++}번째 출력~!")
nextPrintTime += 1_000L // 1초
}
}
}
delay(100L) // 0.1초. 위의 코루틴이 실행될 수 있는 시간을 주는 것임
job.cancel()
}
[main @coroutine#2] 1번째 출력~!
[main @coroutine#2] 2번째 출력~!
[main @coroutine#2] 3번째 출력~!
[main @coroutine#2] 4번째 출력~!
[main @coroutine#2] 5번째 출력~!
Process finished with exit code 0
이렇게 됨. 취소가 안된다는 뜻.
실행 순서 : runBlocking -> launch 다 돌고(중간에 suspend 함수를 사용하지 않고) -> job.cancel
2. CancellationException
코루틴 스스로 취소요청 받으면 CancellationException 던지게끔. (자가진단!)
단, 그냥 하나의 스레드에서 실행시킬 시 위의 그림처럼, launch 코루틴이 자가진단을 한다 하더라도 cancel 명령이 나중에 실행되므로 launch coroutine 과 cancel 명령어를 다른 스레드에서 실행시켜줘야 한다.
따라서 launch(Dispatchers.Default)를 통해 이 코루틴을 다른 스레드에서 실행시킨다.
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
printWithThread("아마 메인쓰레드")
val job = launch(Dispatchers.Default){
var i = 1
var nextPrintTime = System.currentTimeMillis()
while ( i <= 5 ){
if (nextPrintTime <= System.currentTimeMillis()){
printWithThread("${i++}번째 출력~!")
nextPrintTime += 1_000L // 1초
}
if (!isActive){
throw CancellationException()
}
}
}
delay(100L)
job.cancel()
}
[main @coroutine#1] 아마 메인쓰레드
[DefaultDispatcher-worker-1 @coroutine#2] 1번째 출력~!
Process finished with exit code 0
+ isActive : 현재 코루틴이 활성화 되어 있는지, 취소 신호를 받았는지 자가진단 가능하게 해주는 property
위에거는 명백히 취소시키는거고 아래거는 취소신호가 있으면 해당 구문을 빠져나옴으로써 취소 신호를 완료시키는 거라고 할 슈 잇숨.
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
printWithThread("아마 메인쓰레드")
val job = launch(Dispatchers.Default){
var i = 1
var nextPrintTime = System.currentTimeMillis()
while ( isActive && i <= 5 ){
if (nextPrintTime <= System.currentTimeMillis()){
printWithThread("${i++}번째 출력~!")
nextPrintTime += 1_000L // 1초
}
}
}
delay(100L)
job.cancel()
}
3. 사실은 delay()도 CancellationException()을 던져서 코루틴을 종료하는 거쉬엿다?!
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking{
val job = launch{
try{
delay(1_000L) // 1초
} catch (e: CancellationException){
// do nothing
}
printWithThread("delay로 취소가 안되었네 이를 어쩌나~~")
}
delay(100L)
printWithThread("취소시킬게~!")
job.cancel()
}
[main @coroutine#1] 취소시킬게~!
[main @coroutine#2] delay로 취소가 안되었네 이를 어쩌나~~
Process finished with exit code 0
이처럼 delay()를 사용하였음에도 불구하고 launch로 생성된 coroutine이 취소되지 않은걸 볼 수 있다.
이유는 catch에서 delay가 coroutine을 종료시키는 CancellationException을 꿀꺽 해버렸기 때문이다!
이걸 활용해서, catch 대신 finally{}만 사용하여 닫아야하는 자원(?) 혹은 종료시켜야하는 자원을 종료시킬 수 있다.
package coroutine
import kotlinx.coroutines.*
fun main(): Unit = runBlocking{
val job = launch{
try{
delay(1_000L) // 1초
} finally{
// 끝내줘야하는 자원 닫거나 종료시킬 수 있음
}
printWithThread("delay로 취소가 안되었네 이를 어쩌나~~")
}
delay(100L)
printWithThread("취소시킬게~!")
job.cancel()
}
'Kotlin' 카테고리의 다른 글
[Kotlin] Coroutine - Structured Concurrency (0) | 2024.01.12 |
---|---|
[Kotlin] Coroutine의 예외처리 (0) | 2024.01.12 |
[Kotlin] Coroutine - async() (1) | 2024.01.09 |
[Kotlin] Coroutine 디버깅 옵션 (1) | 2024.01.09 |
람다 그리고 코틀린 (0) | 2023.12.20 |
블로그의 정보
Dev_TIMI
its_TIMI