6장의 ‘구조화된 동시성’ 절에서 부모-자식 관계의 다음 특성에 대해 배웠다.
자식이 부모로부터 컨텍스트를 물려받는 건 코루틴 빌더의 가장 기본적인 특징이다.
fun main(): Unit = runBlocking(CoroutineName("main")){
val name = coroutineContext[CoroutineName]?.name
println(name) // main
launch {
delay(1000)
val innerName = coroutineContext[CoroutineName]?.name
println(innerName) // main
}
}
이외에 구조화된 동시성의 중요한 특성 중 세 가지는 Job 컨텍스트와 관련이 있다. Job은 코루틴을 취소하고, 상태를 파악하는 등 다양하게 사용될 수 있다. Job은 정말 중요하고 유용한 Context이므로 이 장과 다음 두 장에서는 Job 컨텍스트 및 Job과 연관된 코틀린 코루틴의 필수적인 작동 방식에 대해 설명한다.
잡(Job)은 수명을 가지고 있으며 취소 가능하다. Job은 인터페이스이긴 하지만 구체적인 사용법과 상태를 가지고 있다는 점에서 추상 클래스처럼 다룰 수도 있다.
Job의 수명은 상태로 나타낸다. 다음은 잡의 상태와 상태 변화를 나타낸 도식도다.
Active
상태에서는 Job이 실행되고 코루틴은 Job을 수행한다. Job이 코루틴 빌더에 의해 생성되었을 때 코루틴의 body가 실행되는 상태다. 이 상태에서 자식 코루틴을 시작할 수 있다. 대부분의 코루틴은 Active
상태로 시작된다. 지연 시작되는 코루틴만 New
상태에서 시작된다. New
상태인 코루틴이 Active
상태가 되려면 작업이 실행되어야 한다. 코루틴이 body를 실행하면 Active
상태로 가게 된다.
실행이 완료되면 상태는 Completing
으로 바뀌고 자식들을 기다린다. 자식들의 실행도 모두 끝났다면 Job은 마지막 상태인 Completed
로 바뀐다. 만약 Job이 실행 도중에(Active
또는 Completing
상태) 취소되거나 실패하게 되면 Cancelling
상태로 가게 된다. 여기서 연결을 끊거나 자원을 반납하는 등의 후처리 작업을 할 수 있다.(다음 장에서 어떻게 후처리 작업을 하는지 살펴본다.) 후처리 작업이 완료되면 Job은 Cancelled
상태가 된다.
Job의 상태는 toString 메서드로 볼 수 있다.(toString은 디버깅과 로깅 목적으로만 사용되어야 하며, 함수의 규약을 깨버릴 수 있기 때문에 코드에서 파싱해서 의미를 부여하면 안 된다. 관련 내용은 Effective Kotlin 참고) 아래 예제에서 상태가 바뀔 때 Job 또한 다르다는 걸 확인할 수 있다. 마지막 Job은 지연 시작되기 때문에 저절로 시작되지 않는다. 다른 모든 잡은 생성되는 즉시 Active 상태가 된다.
다음 코드는 Job의 여러 가지 상태를 보여 주고 있다. join은 코루틴이 완료되는 걸 기다리기 위해 사용되었다.
suspend fun main() = coroutineScope {
val job = Job() // 빌더로 생성된 잡은 메서드로 완료시킬 때까지 Active 상태다.
println(job) // JobImpl{Active}@4c98385c
job.complete()
println(job) // JobImpl{Completed}@4c98385c
// launch는 기본적으로 활성화되어 있다.
val activeJob = launch {
delay(1000)
}
println(activeJob) // StandaloneCoroutine{Active}@1bc6a36e
activeJob.join() // join은 코루틴이 완료되는 것을 기다리기 위해 사용
println(activeJob) // StandaloneCoroutine{Completed}@1bc6a36e
// launch는 New 상태로 지연 시작된다.
val lazyJob = launch(start = CoroutineStart.LAZY) {
delay(1000)
}
println(lazyJob) // LazyStandaloneCoroutine{New}@4771b499
lazyJob.start() // Active 상태가 되려면 시작하는 함수를 호출해야 한다.
println(lazyJob) // LazyStandaloneCoroutine{Active}@4771b499
lazyJob.join()
println(lazyJob) // LazyStandaloneCoroutine{Completed}@4771b499
}