Dev_TIMI

[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시간 코루틴

2. CancellationException

코루틴 스스로 취소요청 받으면 CancellationException 던지게끔. (자가진단!)

 

단, 그냥 하나의 스레드에서 실행시킬 시 위의 그림처럼, launch 코루틴이 자가진단을 한다 하더라도 cancel 명령이 나중에 실행되므로 launch coroutine 과 cancel 명령어를 다른 스레드에서 실행시켜줘야 한다. 

 

따라서 launch(Dispatchers.Default)를 통해 이 코루틴을 다른 스레드에서 실행시킨다.

출처 : 최태현 - 2시간 코루틴

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

활동하기