일시 중단 함수란 무엇인가?
suspend fun으로 선언되는 일시 중단 함수는 함수 내에 일시 중단 지점을 포함할 수 있는 특별한 함수이다. 코루틴은 언제든지 일시 중단하고, 스레드를 양보할 수 있다는 것을 기억하자. 일시 중단 함수는 코루틴에서 실행되는 일시 중단 지점이 포함된 코드들을 재사용할 수 있는 코드의 집합으로 만드는 역할을 한다.
예를 들어 다음과 같은 코드를 살펴보자.
fun main() = runBlocking<Unit> {
delay(100L)
println("Hello Coroutines")
delay(100L)
println("Hello Coroutines")
}
이 코드에서는 delay 함수와 println 함수가 반복된다. 따라서 이 코드는 다음과 같이 함수로 만들 수 있다.
fun delayAndPrintHelloCoroutines() {
delay(100L)
println("Hello Coroutines")
}
하지만, 이 함수를 IDE에서 살펴보면, 오류가 나는 것을 볼 수 있다.
이 오류를 살펴보면, 일시 중단 함수인 delay는 일시 중단 함수에서만 호출될 수 있다는 의미이다. 왜 이런 오류가 나는 것일까? 바로 delay 함수는 코루틴이 일정 시간 동안 스레드를 양보하도록 만드는 함수여서 일시 중단 함수로 선언되었기 때문이다. 따라서 이 함수를 호출하는 곳도 일시 중단 함수여야 한다.
따라서 이 오류는 단순히 fun을 suspend fun으로 바꿈으로써 해결할 수 있다.
그러면 이제 위의 함수를 다음과 같이 바꿀 수 있게 된다.
일시 중단 함수는 코루틴이 아니다.
그렇다면, 다음 main 함수를 실행하는데 시간은 얼마나 걸릴까? 한 번 맞쳐보자.
fun main() = runBlocking<Unit> {
delayAndPrintHelloCoroutines()
delayAndPrintHelloCoroutines()
}
suspend fun delayAndPrintHelloCoroutines() {
delay(100L)
println("Hello Coroutines")
}
정답은 바로 200밀리초이다. 일시 중단 함수가 호출된다고 해서 새로운 코루틴이 생성되지 않기 때문에 100밀리초를 대기하고 "Hello Coroutines"가 프린트 되는 것이 하나의 코루틴에서 실행되기 때문이다. 이를 확인하기 위해 다음 코드를 실행해보면 200밀리초보다 약간 더의 이간이 걸리는 것을 확인할 수 있다.
fun main() = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
delayAndPrintHelloCoroutines()
delayAndPrintHelloCoroutines()
println("${System.currentTimeMillis() - startTime}") // 211 출력
}
만약 일시 중단 함수가 새로운 코루틴에서 실행되려면, 다음 코드와 같이 일시 중단 함수를 앞서 다룬 launch나 async같은 코루틴 빌더로 감싸야 한다.
fun main() = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
val job1 = launch {
delayAndPrintHelloCoroutines()
}
val job2 = launch {
delayAndPrintHelloCoroutines()
}
job1.join()
job2.join()
println("${System.currentTimeMillis() - startTime}") // 110 출력
}
그러면 각 일시 중단 함수가 병렬로 실행돼 총 실행 시간이 110 밀리로 정도의 시간이 걸리는 것을 볼 수 있다.
일시 중단 함수의 호출 가능 위치
일시 중단 함수의 호출 가능 위치는 두 군데이다. 하나는 다른 일시 중단 함수 내부이며, 다른 하나는 코루틴 내부이다. 즉, 일시 중단 함수는 일시 중단이 가능한 위치에서만 호출된다.
일시 중단 함수 내부에서 일시 중단 함수 호출
앞서 다룬 코드를 다시 한 번 살펴보자.
fun main() = runBlocking<Unit> {
delayAndPrintHelloCoroutines()
delayAndPrintHelloCoroutines()
}
suspend fun delayAndPrintHelloCoroutines() {
delay(100L)
println("Hello Coroutines")
}
이 코드에서 delayAndPrintHelloCoroutines가 fun으로 선언되면, 오류가 발생했었다. 이유는 delay 함수가 일시 중단 함수였기 때문이다. 여기에서 볼 수 있듯이 일시 중단 함수는 일시 중단 함수에서 호출 가능하다.
코루틴에서 일시 중단 함수
그렇다면, 가장 상위의 일시 중단 함수는 어디에서 호출돼야 할까? 바로 코루틴이다. 일반 함수는 한 번 실행되면 끝까지 실행될 뿐 '일시 중단' 이라는 기능 자체가 없다. '일시 중단' 기능은 코루틴에만 있는 특별한 기능이기 때문에 코루틴에서 호출돼야 한다. 위의 코드에서는 runBlocking 코루틴 내부에서 일시 중단 함수가 호출되고 있다.
정리
- suspend fun는 일시 중단 가능한 함수로, 해당 함수 내에 일시 중단이 가능한 작업이 있다는 것을 뜻한다.
- 따라서 suspend fun은 코루틴 내부에서 또는 suspend fun 내부에서만 사용할 수 있다.
Kotlin Coroutines 공식 기술 문서 번역이 GitHub 오픈소스로 배포되었습니다. Starganizer가 되어 오픈소스를 지지해주세요.