개발일지

Kotlin in A..Z - Coroutine(3) Cancel 본문

Kotlin (코틀린)

Kotlin in A..Z - Coroutine(3) Cancel

강태종 2020. 10. 15. 22:05

cancel()

Job을 취소시키는 함수, Job이 취소되었을 때 suspend 함수를 호출하면 JobCancellationException을 발생시킨다.

fun main() = runBlocking {
    val job = launch {
        repeat(5) {
            try {
                delay(1000L)
                println("Job Run Index($it)")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    delay(3000L)
    job.cancel()
    job.join()
}
Job Run Index(0)
Job Run Index(1)
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@3ecf72fd
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@3ecf72fd
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@3ecf72fd

Process finished with exit code 0

 

cancelAndJoin()

cancel()과 join()을 호출하는 코드

public suspend fun Job.cancelAndJoin() {
    cancel()
    return join()
}

Job이 취소됐다고 바로 중단되는 것이 아니다. Job이 취소되어도 suspend함수를 호출해서 예외를 발생하거나 isActive와 같은 상태를 저장하는 변수로 내부에서 중단시켜야 한다.

fun main() = runBlocking {
    val job = launch {
        repeat(5) {
            println("isRunning")
            Thread.sleep(1000L)
        }
    }

    delay(1000L)
    job.cancel()
    job.join()
}
isRunning
isRunning
isRunning
isRunning
isRunning

Process finished with exit code 0

yield()

delay로 exception을 발생시키는 것보다 yield로 예외를 발생시키자.

fun main() = runBlocking {
    var time = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var i = 0
        try {
            while (i < 5) {
                yield()
                if (System.currentTimeMillis() > time) {
                    println("isRunning")
                    time += 1000L
                    i++
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
    delay(1000L)
    job.cancelAndJoin()
}
isRunning
isRunning
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@13503210

Process finished with exit code 0

isActive

isActive상태 검사로 coroutine을 협력적으로 종료하자.

import kotlinx.coroutines.*
import java.lang.Exception

fun main() = runBlocking {
    var time = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        while (isActive) {
            if (System.currentTimeMillis() > time) {
                println("isRunning")
                time += 1000L
            }
        }
    }
    delay(1000L)
    job.cancelAndJoin()
}
isRunning
isRunning

Process finished with exit code 0

NonCancellable

Job이 Cancelled됐지만 suspend함수를 호출시켜야 할 때

fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1000) {
                println("isRunning")
                delay(500L)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            withContext(NonCancellable) {
                println("into finally")
                delay(1000L)
                println("Call suspend function what you need")
                delay(1000L)
            }
        }
    }

    delay(1000L)
    job.cancelAndJoin()
}
isRunning
isRunning
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@7a92922
into finally
Call suspend function what you need

Process finished with exit code 0

withTimeout

timeout을 설정하는 Coroutine Scope이다.

TimeOut이 발생하면 Exception을 발생시킨다.

fun main() = runBlocking {
    withTimeout(3000L) {
        repeat(1000) {
            println("isRunning")
            delay(1000L)
        }
    }

    println("Exit")
}
isRunning
isRunning
isRunning
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 3000 ms
	at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:158)
	at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:128)
	at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run(EventLoop.common.kt:497)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
	at kotlinx.coroutines.DefaultExecutor.run(DefaultExecutor.kt:68)
	at java.base/java.lang.Thread.run(Thread.java:832)

Process finished with exit code 1

withTimeoutOrNull

timeout이 발생하여 종료하면 null을 반환한다.

fun main() = runBlocking {
    val result = withTimeoutOrNull(3000L) {
        repeat(1000) {
            println("isRunning")
            delay(1000L)
        }

        "Done"
    }

    println("Exit : $result")
}
isRunning
isRunning
isRunning
Exit : null

Process finished with exit code 0

 

Comments