[Coroutines] 코루틴의 상태와 Job의 상태 변수

hxeyexn·2025년 12월 15일

Kotlin Coroutines

목록 보기
6/6

들어가기 전
이번 글의 전반적인 내용은 『코틀린 코루틴의 정석』이라는 책을 읽으며 이해한 내용을 바탕으로 작성한다. 정보를 전달하기보다는, 필자가 이해한 바를 정리해 기록하기 위한 목적이 크다.

Intro

오늘은 지난 시간에 이어 코루틴의 상태와 Job의 상태 변수에 대해 알아보겠다.

코루틴을 사용할 때, 코루틴의 상태를 인지하며 사용하는 것과 상태에 대한 고려 없이 사용하는 것 사이에는 분명한 차이가 있다고 생각한다.

예를 들어, 이미지를 한 장만 업로드할 수 있는 상황이 있다고 가정해보자. 이미지가 업로드되는 도중에 사용자가 다른 이미지를 선택하면 이전 업로드 작업은 취소되어야 한다.

만약 코루틴의 상태를 고려하지 않고 새로운 작업을 실행한다면, 이전 작업이 끝나지 않은 상태에서 또 다른 코루틴이 실행될 수 있다. 결과적으로 불필요한 작업이 동시에 수행되거나, 의도하지 않은 결과가 발생할 수 있다.

반면 코루틴의 상태에 대한 지식이 있다면 현재 실행 중인 작업이 있는지 확인하고 이를 취소하는 방식으로 간단하게 처리할 수 있다.



코루틴의 6가지 상태

코루틴은 6가지 상태, '생성', '실행 중', '실행 완료 중', '실행 완료', '취소 중', '취소 완료'를 가질 수 있다.

생성(New)

코루틴이 생성되었지만 아직 실행되지 않은 상태를 생성 상태라고 한다.
코루틴 빌더를 통해 코루틴을 생성하면 코루틴은 기본적으로 생성과 동시에 실행 중 상태로 전환된다.

그렇다면 생성 상태에 머무르는 코루틴은 어떻게 만들 수 있을까?
코루틴 빌더의 start 인자에 CoroutineStart.Lazy를 전달하면, 실행을 지연한 코루틴을 생성할 수 있다.

실행 중(Active)

앞서 말했듯이 코루틴 빌더를 통해 코루틴을 생성하면 기본적으로 생성과 동시에 실행 중 상태로 전환된다. 이때 실행 중 상태는 코루틴이 실제로 실행 중일 때뿐만 아니라 delay와 같은 일시 중단 함수로 인해 잠시 중단된 경우도 포함한다.

실행 완료 중(Completing)

실행 완료 중 상태는 부모 코루틴의 모든 코드가 실행되었지만, 자식 코루틴이 실행 중인 경우에 부모 코루틴이 가지는 상태이다.

부모 코루틴은 더 이상 실행할 코드가 없더라도, 모든 자식 코루틴이 완료되기 전까지는 실행 완료 상태로 전환될 수 없어 '실행 완료 중' 상태에 머문다. 이후 자식 코루틴이 모두 실행을 마치면 부모 코루틴은 자동으로 '실행 완료' 상태로 전환된다.

실행 완료(Completed)

코루틴의 모든 코드가 실행되면 실행 완료 상태로 전환된다. 이 상태에서는 더 이상 코루틴이 실행되거나 재개되지 않으며, 이미 종료된 작업으로 취급한다.

취소 중(Cancelling)

Job.cancel() 등을 통해 코루틴에 취소가 요청되면 코루틴은 취소 중 상태로 전환된다. 이 상태는 아직 취소가 완료된 것은 아니며, 취소를 인지하기 전까지 코루틴은 계속해서 실행될 수 있다.

취소 완료(Cancelled)

코루틴이 취소 확인 시점(일시 중단 등)에 취소를 인지하면, 취소 완료 상태로 전환된다. 이 상태에서는 코루틴이 더 이상 실행되거나 재개되지 않는다.



Job의 상태 변수

앞서 코루틴이 6가지 상태를 가진다는 것을 살펴보았다. 그렇다면 이러한 상태들은 어떻게 확인할 수 있을까? 코루틴의 상태는 Job 객체를 통해 확인할 수 있다.
Job 객체는 코루틴이 어떤 상태에 있는지 나타내는 상태 변수들을 외부로 공개한다.

다만, Job 객체는 코루틴을 추상화한 객체이므로 내부 상태를 직접 노출하지 않고 코루틴의 상태를 간접적으로만 나타낸다. Job 객체가 외부로 제공하는 상태 변수는 isActive, isCancelled, isCompleted 세 가지가 있다.

isActive

  • 코루틴이 활성화돼 있는지의 여부
  • 활성화돼 있다는 것은 코루틴이 실행된 후 취소가 요청X, 실행이 완료되지 않은 상태라는 의미
  • 취소가 요청되거나 실행이 완료된 코루틴은 활성화되지 않은 것으로 봄

isCancelled

  • 코루틴이 취소 요청됐는지의 여부
  • 코루틴이 취소 요청되기만 하면 true가 반환되므로 isCancelled가 true이더라도 즉시 취소되는 것은 아님

isCompleted

  • 코루틴 실행이 완료됐는지의 여부
  • 코루틴의 모든 코드가 실행 완료되거나 취소 완료되면 true를 반환
  • 실행 중인 상태에서는 false를 반환


Job의 상태 확인해보기

이제 코루틴의 6가지 상태를 직접 코드로 작성해 보고 각 상태에서 Job의 상태 값이 어떻게 달라지는지 확인해보자.

생성 상태의 코루틴

생성 상태의 코루틴을 생성하기 위해서는 코루틴 빌더의 start 인자로 CoroutineStart.Lazy를 넘겨 지연 시작이 적용된 코루틴을 생성해야 한다.

// Job의 상태를 출력하는 함수
fun printJobState(job: Job) {  
    println(  
        "Job State\n" +  
            "isActive >> ${job.isActive}\n" +  
            "isCancelled >> ${job.isCancelled}\n" +  
            "isCompleted >> ${job.isCompleted} "  
    )  
}

fun main() = runBlocking<Unit> {  
    val job: Job = launch(start = CoroutineStart.LAZY) { // 생성 상태의 Job 생성  
        delay(1_000L)  
    }  
    printJobState(job)  
}

위 코드를 실행해 보면 코루틴이 생성만 된 상태로 실행·취소·완료되지 않았기 때문에 3가지 상태 값 모두 false로 출력되는 것을 확인할 수 있다.

Job State
isActive >> false  // 코루틴이 생성된 후 실행되지 않았기 때문에 false
isCancelled >> false  // 취소가 요청되지 않았기 때문에 false
isCompleted >> false  // 실행 완료되지 않았기 때문에 false

실행 중 상태의 코루틴

코루틴 빌더로 코루틴을 생성하면 CoroutineDispatcher에 의해 스레드로 보내져 실행된다. 이때 코루틴이 실행되고 있는 상태를 '실행 중' 상태라고 부른다.

fun main() = runBlocking<Unit> {  
    val job: Job = launch {
        delay(1_000L)  
    }  
    printJobState(job)  
}

실행해 보면 코루틴 빌더의 start 인자로 CoroutineStart.Lazy를 넘겨 지연 시작이 적용된 코루틴과는 달리 isActive가 true인 것을 확인할 수 있다.

Job State
isActive >> true  // 코루틴이 생성된 후 취소되거나 완료되지 않았으므로 true
isCancelled >> false  // 취소가 요청되지 않았기 때문에 false
isCompleted >> false  // 실행 완료되지 않았기 때문에 false

실행 완료 중 상태의 코루틴

실행 완료 중 상태를 정확히 이해하기 위해서는 코루틴의 구조화된 동시성에 대한 지식이 필요하다. 하지만 이번 포스트에서는 구조화된 동시성을 다루지 않으므로, 부모 코루틴의 실행은 끝났지만 자식 코루틴이 실행 중일 때의 상태라는 점만 짚고 넘어가자.


실행 완료 상태의 코루틴

실행 완료 상태의 코루틴은 코루틴을 실행한 뒤, 해당 작업이 완료될 때까지 대기하면 확인할 수 있다.

fun main() = runBlocking<Unit> {  
    val job: Job = launch {
        delay(1_000L)  
    }  
    // 2초가 지난 시점에는 이미 1초간 실행되는 코루틴이 실행 완료된 상태
    delay(2_000L) 
    printJobState(job)  
}
Job State
isActive >> false  // 코루틴이 실행 완료돼 활성화된 상태가 아니므로 false
isCancelled >> false  // 취소가 요청되지 않고 정상적으로 실행 완료됐으므로 false
isCompleted >> true  // 실행 완료되었기 때문에 false

취소 중인 코루틴

코루틴에 단순히 취소를 요청하면 코루틴은 취소 중 상태를 거쳐 곧바로 취소 완료 상태로 변화한다. 따라서 취소를 확인할 수 있는 시점이 없는 코루틴을 생성하고 취소를 요청하면 취소 중 상태의 코루틴을 확인할 수 있다.

fun main() = runBlocking<Unit> {  
    val whileJob: Job = launch(Dispatchers.Default) {   
        while(this.isActive) {  
            // 작업 실행
        }  
    }  
    whileJob.cancel()  
    printJobState(job)  
}
Job State
isActive >> false  // 취소 요청된 코루틴은 활성화돼 있지 않다고 봐서 false
isCancelled >> true  // 취소가 요청됐으므로 true
isCompleted >> false  // 취소가 완료되지 않았으므로 false

취소 완료된 코루틴

코루틴은 코루틴에 취소가 요청되고 취소 요청이 확인되는 시점에 취소가 완료된다.

fun main() = runBlocking<Unit> {  
    val job: Job = launch {
        delay(5_000L)  
    }  
    job.cancelAndJoin() // 코루틴 취소 요청 + 취소가 완료될 때까지 대기
    printJobState(job)  
}
Job State
isActive >> false  // 취소 완료돼 코루틴이 활성화돼 있지 않으므로 false
isCancelled >> true  // 취소 요청 후 취소가 완료됐으므로 true
isCompleted >> true  // 취소 요청 후 취소가 완료됐으므로 true


Outro

지금까지 코루틴의 상태와 Job의 상태 변수를 살펴보았다.
이를 이해하면 코루틴을 사용하는 도중 발생하는 문제를 보다 쉽게 파악할 수 있다.

요약

코루틴 상태isActiveisCanceledisCompleted
생성(New)falsefalsefalse
실행 중(Active)truefalsefalse
실행 완료 중(Completed)truefalsefalse
실행 완료(Completed)falsefalsetrue
취소 중(Cancelling)falsetruefalse
취소 완료(Cancelled)falsetruetrue


참고자료

조세영, 『코틀린 코루틴의 정석』

profile
Android Developer

0개의 댓글