Job 취소
import kotlinx.coroutines.*
suspend fun doOneTwoThree() = coroutineScope {
val job1 = launch {
println("launch1: ${Thread.currentThread().name}")
delay(1000L)
println("3!")
}
val job2 = launch {
println("lanch2: ${Thread.currentThread().name}")
println("1!")
}
val job3 = launch {
println("launch3: ${Thread.currentThread().name}")
println("2!")
}
delay(800L)
job1.cancel()
job2.cancel()
job3.cancel()
println("4!")
}
fun main() = runBlocking {
doOneTwoThree()
println("runBlocking: ${Thread.currentThread().name}")
println("5!")
}
launch1: main @coroutine#2
lanch2: main @coroutine#3
1!
launch3: main @coroutine#4
2!
4!
runBlocking: main @coroutine#1
5!
명시적인 Job에 대해 cancel 메서드를 호출해 취소할 수 있다.
취소 불가능한 Job
import kotlinx.coroutines.*
suspend fun doCount() = coroutineScope {
val job1 = launch(Dispatchers.Default) {
var i = 1
var nextTime = System.currentTimeMillis() + 100L
while(i <= 10) {
val currentTime = System.currentTimeMillis()
if(currentTime >= nextTime) {
println(i)
nextTime = currentTime + 100L
i++```
}
}
}
delay(200L)
job1.cancel()
println("doCount Done!")
}
fun main() = runBlocking {
doCount()
}
1
2
doCount Done!
3
4
5
6
7
8
9
10
취소든 종료든 끝난 이후에 doCountDone을 출력하고 싶었지만 취소가 되지 않은 코드이다.
cancel & join
import kotlinx.coroutines.*
suspend fun doCount() = coroutineScope {
val job1 = launch(Dispatchers.Default) {
var i = 1
var nextTime = System.currentTimeMillis() + 100L
while(i <= 10) {
val currentTime = System.currentTimeMillis()
if(currentTime >= nextTime) {
println(i)
nextTime = currentTime + 100L
i++
}
}
}
delay(200L)
job1.cancel()
job1.join()
println("doCount Done!")
}
fun main() = runBlocking {
doCount()
}
1
2
3
4
5
6
7
8
9
10
doCount Done!
cancel 이후에 join을 넣어서 취소가 된 이후에 출력되게끔 하였다.
cancelAndJoin
import kotlinx.coroutines.*
suspend fun doCount() = coroutineScope {
val job1 = launch(Dispatchers.Default) {
var i = 1
var nextTime = System.currentTimeMillis() + 100L
while(i <= 10) {
val currentTime = System.currentTimeMillis()
if(currentTime >= nextTime) {
println(i)
nextTime = currentTime + 100L
i++
}
}
}
delay(200L)
job1.cancelAndJoin()
println("doCount Done!")
}
fun main() = runBlocking {
doCount()
}
1
2
3
4
5
6
7
8
9
10
doCount Done!
cancel을 하고나서 join을 하는 일이 자주 있기 때문에 한 번에 할 수 있게끔
cancelAndJoin을 쓰면 된다.
cancel이 가능한 코루틴
import kotlinx.coroutines.*
suspend fun doCount() = coroutineScope {
val job1 = launch(Dispatchers.Default) {
var i = 1
var nextTime = System.currentTimeMillis() + 100L
while(i <= 10 && isActive) {
val currentTime = System.currentTimeMillis()
if(currentTime >= nextTime) {
println(i)
nextTime = currentTime + 100L
i++
}
}
}
delay(200L)
job1.cancelAndJoin()
println("doCount Done!")
}
fun main() = runBlocking {
doCount()
}
1
doCount Done!
isActive를 호출하면 해당 코루틴이 활성화상태인지 알 수 있다.
finally
import kotlinx.coroutines.*
suspend fun doOneTwoThree() = coroutineScope {
val job1 = launch {
try {
println("launch1 :${Thread.currentThread().name}")
delay(1000L)
println("3!")
} finally {
println("job1 is finishing")
}
}
val job2 = launch {
try {
println("launch2: ${Thread.currentThread().name}}")
delay(1000L)
println("1!")
}finally {
println("job2 is finishing")
}
}
val job3 = launch {
try {
println("launch3: ${Thread.currentThread().name}")
delay(1000L)
println("2!")
}finally {
println("job3 is finishing")
}
}
delay(800L)
job1.cancel()
job2.cancel()
job3.cancel()
println("4!")
}
fun main() = runBlocking {
doOneTwoThree()
println("runBlocking: ${Thread.currentThread().name}")
println("5!")
}
launch1 :main @coroutine#2
launch2: main @coroutine#3}
launch3: main @coroutine#4
4!
job1 is finishing
job2 is finishing
job3 is finishing
runBlocking: main @coroutine#1
5!_
suspend 함수들은 JobCancellationException을 발생하기 때문에 try catch finally로 대응할 수 있다.
취소 불가능하게
import kotlinx.coroutines.*
suspend fun doOneTwoThree() = coroutineScope {
val job1 = launch {
withContext(NonCancellable) {
println("launch1 :${Thread.currentThread().name}")
delay(1000L)
println("3!")
}
delay(1000L)
print("job1: end")
}
val job2 = launch {
withContext(NonCancellable) {
println("launch2: ${Thread.currentThread().name}}")
delay(1000L)
println("1!")
}
delay(1000L)
print("job2: end")
}
val job3 = launch {
withContext(NonCancellable) {
println("launch3: ${Thread.currentThread().name}")
delay(1000L)
println("2!")
}
delay(1000L)
print("job3: end")
}
delay(800L)
job1.cancel()
job2.cancel()
job3.cancel()
println("4!")
}
fun main() = runBlocking {
doOneTwoThree()
println("runBlocking: ${Thread.currentThread().name}")
println("5!")
}
launch1 :main @coroutine#2
launch2: main @coroutine#3}
launch3: main @coroutine#4
4!
3!
1!
2!
runBlocking: main @coroutine#1
5!
withContext(NonCancellable)을 이용하면 취소 불가능한 블록을 만들 수 있다.
withContext블록 밖에 있는 jobx: end 구문은 취소되어 출력되지 않는다.
타임 아웃
import kotlinx.coroutines.*
suspend fun doCount() = coroutineScope {
val job1 = launch(Dispatchers.Default) {
var i = 1
var nextTime = System.currentTimeMillis() + 100L
while(i <= 10 && isActive) {
val currentTime = System.currentTimeMillis()
if(currentTime >= nextTime) {
println(i)
nextTime = currentTime + 100L
i++
}
}
}
}
fun main() = runBlocking {
withTimeout(500L) {
doCount()
}
}
1
2
3
4
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 500 ms
at (Coroutine boundary. (:-1)
at FileKtmain$1$1.invokeSuspend (File.kt:-1) at FileKtmainDelayedRunnableTask .run(EventLoop.common.kt:508)
일정 시간이 끝난 후에 종료하고 싶다면 withTimeout을 이용할 수 있다.
취소가 되면 TimeoutCancellationException 예외가 발생한다.
withTimeoutOrNull
import kotlinx.coroutines.*
suspend fun doCount() = coroutineScope {
val job1 = launch(Dispatchers.Default) {
var i = 1
var nextTime = System.currentTimeMillis() + 100L
while(i <= 10 && isActive) {
val currentTime = System.currentTimeMillis()
if(currentTime >= nextTime) {
println(i)
nextTime = currentTime + 100L
i++
}
}
}
}
fun main() = runBlocking {
val result = withTimeoutOrNull(500L) {
doCount()
true
} ?: false
println(result)
}
1
2
3
4
false
성공할경우 withTimeoutOrNull의 마지막에서 true를 리턴하게 하고 실패한경우 null을 반환하게 되므로 엘비스 연산자를 이용해 false 를 리턴하게 하였다.