Coroutine ‐ Understanding Coroutines - thought-corner/Backend-PlayGround GitHub Wiki
- 서브루틴이란, 함수(루틴) 내에서 함수가 호출될 경우 호출된 함수를 서브루틴이라고 한다.
- 루틴에 의해 서브루틴이 호출되면 서브루틴의 실행이 완료될 때까지 다른 작업을 할 수 없는 것과 다르게 코루틴은 함께 실행되는 루틴으로 서로 간에 쓰레드 사용을 양보하며 함께 실행된다.
- 코루틴은 작업 중간에 쓰레드 사용이 필요가 없어지면 쓰레드를 양보한다. 이 떄, 양보된 쓰레드는 다른 코루틴이 점유해 사용할 수 있다.
- 쓰레드를 양보하는 주체는 코루틴이다. 따라서 쓰레드를 양보하기 위해서는 코루틴이 직접 쓰레들 양보를 위한 함수를 호출해야 한다.
fun main(): Unit = runBlocking<Unit> {
val startTime: Long = System.currentTimeMillis()
repeat(10) { repeatTime ->
launch {
delay(1000L) // 1초 동안 코루틴 일시 중단
println("[${getElapsedTime(startTime)}] 코루틴${repeatTime} 실행 완료")
}
}
}
fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms"- 작업을 일정 시간 동안 일시 중단해야 할 경우
delay()함수를 사용할 수 있다. - 코루틴이
delay()함수를 호출하면 코루틴은 사용하던 쓰레드를 양보하고 설정된 시간 동안 일시 중단된다. - 여기서
Thread.sleep()을 사용하면 쓰레드를 양보하지 않는다. 코루틴이 양보의 주체가 되는 것이지 쓰레드가 양보의 주체가 되는 것이 아니다.
-
join()함수나await()함수가 호출되면 해당 함수를 호출한 코루틴은 쓰레드를 양보하고join()또는await()의 대상이 된 코루틴이 완료될 때까지 일시 중단된다. - 하나의 코루틴이 쓰레드를 양보하지 않으면 다른 코루틴은 쓰레드를 점유하지 못한다.
-
yield()함수를 통해 직접 쓰레드를 양보하는 경우도 필요하다. - 단일 쓰레드만 사용하는 상황에서 명시적으로
yield()를 사용하는 경우가 종종 생긴다.
1. 취소 안되는 코드 - 같은 쓰레드 공유(디스패처 X)
-
launch()내부에 디스패처를 적지 않으면 부모 코루틴인runBlocking()의 컨텍스트를 그대로 상속받는다. -
runBlocking()은 메인 쓰레드만을 사용한다. -
runBlocking()코루틴이 메인 쓰레드에서 실행되다가delay(100L)을 만나면 메인 쓰레드의 사용권을 양보한다. - 대기하고 있던
launch()코루틴이 메인 쓰레드의 제어권을 넘겨받아 while문을 실행한다. - while문 안에 일시 중단 함수(delay나 yield)가 없기 때문에 일반적인 연산만 무한 반복하므로 메인 쓰레드를 쥐고 절대 놓아주지 않는다.
- 100ms가 지나
runBlocking()이 깨어나려 해도, 메인 쓰레드를launch()코루틴이 점유하고 있어 깨어날 수가 없다. -
job.cancel()코드에 도달하지 못하므로 취소가 불가능하다.
2. 취소되는 코드 - 쓰레드 분리(디스패처 O)
-
launch()내부에Dispatchers.Default를 지정하면 이 코루틴은 부모와 쓰레드를 공유하지 않고 백그라운드 쓰레드 풀에서 완전히 독립적으로 실행된다. -
runBlocking()코루틴은 메인 스레드에서,launch()코루틴은 백그라운드 워커 쓰레드에서 각각 동시에 실행된다. -
runBlocking()이delay(100L)을 만나면 메인 쓰레드에서 100ms동안 잠들게 된다. - 100ms 뒤, 메인 쓰레드가 정상적으로 깨어나 다음 줄인
whileJob.cancel()을 호출한다. - 백그라운드 워커 쓰레드에서 돌고 있던 while문에서
isActive를 검사하는데 메인 쓰레드에서 취소 요청을 했기 때문에isActive가 false가 되면서 루프를 탈출하고 종료하게 된다.
- 코루틴이 일시 중단된 후 재개되면
CoroutineDispatcher객체는 재개된 코루틴을 다시 쓰레드에 보낸다. - 이 때,
CoroutineDispatcher객체는 코루틴을 사용할 수 있는 쓰레드 중 하나에 보내기 때문에 코루틴이 재개된 후의 실행 쓰레드는 일시 중단되기 전의 쓰레드와 다를 수가 있다.