이 글은 아래 링크의 내용을 기반으로 하여 설명합니다.
https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md
또한 예제에서 로그 print시 println과 안드로이드의 Log.e()를 혼용합니다.
Cancelling coroutine execution
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancel() // cancels the job
job.join() // waits for job's completion
println("main: Now I can quit.")
}
repeat으로 1000번 반복하지만 1300ms 이후에 취소되므로 아래와 같은 결과가 나타납니다.
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
main: I'm tired of waiting!
main: Now I can quit.
job을 cancel후 join 시켰지만 cancelAndJoin 명령으로 한번에 처리 할 수도 있습니다.
Cancellation is cooperative
fun main() = runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5) { // computation loop, just wastes CPU
// yield() //여기에 추가하면 정상적으로 취소된다
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
- yield function을 이용하여 추기적으로 취소를 체크하는 suspend function을 invoke 시킨다.
- 명시적으로 취소 상태를 체크한다. (isActive 이용)
fun main() = runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // cancellable computation loop
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
위 예제처럼 isActivie를 이용하면 loop도 취소 가능합니다.
isActive는 확장된 property로, CoroutineScope object를 통하여 coroutine code 내부에서 사용할수 있습니다.
Closing resources with finally
fun main() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
} finally {
println("I'm running finally")
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
위 코드의 결과는 아래와 같습니다.Run non-cancellable block
fun main() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("I'm running finally")
delay(1000L)
println("And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
Timeout
fun main() = runBlocking {
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
실제로 Exception을 발생시키면서 main()이 끝납니다.
안드로이드에서는 try-catch 처리를 하지 않으면 앱이 중지되면서 끝납니다.
위 예제와 같이 cancel() 또는 cancelAndJoin()으로 coroutine을 취소하는 경우에는 try-catch를 하지 않아도 앱은 정상 종료됩니다.
따라서 android에서 withTimeout 쓸때는 꼭 try-catch로 묶어서 사용해야 앱이 중지 되는걸 방지할 수 있습니다.
fun withTimetoutTest() {
runBlocking {
try {
withTimeout(10000L) {
repeat(1000) { i ->
Log.e(TAG, "I'm sleeping $i...")
delay(500L)
}
}
} catch (te: TimeoutCancellationException) {
Log.e(TAG, "Timetout!!!")
}
}
}
fun main() = runBlocking {
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // will get cancelled before it produces this result
}
println("Result is $result")
}
결과는 아래와 같습니다.
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Result is null
withTimeout과 withTimeoutOrNull은 코드블럭 내부의{...} 마지막값을 return 합니다.
'개발이야기 > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 - 코루틴#4 - context와 dispatchers (0) | 2018.12.09 |
---|---|
[Kotlin] 코틀린-코루틴#3 - suspending function의 구성 (0) | 2018.12.06 |
[Kotlin] 코틀린 - 코루틴#1 기본! (3) | 2018.12.03 |
[Kotlin] 코틀린 constructor vs init block (2) | 2018.05.29 |
[Kotlin] 코틀린 Generic #1 (1) | 2018.05.12 |