| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 코루틴
- 코틀린
- Abstract Factory
- 옵저버 패턴
- Coroutines
- ㅋㅁ
- 코틀린멀티플랫폼
- 디자인패턴 #
- Functional Programming
- 빌터패턴
- 추상팩토리패턴
- material3
- 팩토리 메소드
- Design Pattern
- kotlin multiplatform
- compose
- android designsystem
- builderPattern
- 안드로이드 디자인시스템
- Kotlin
- PrototypePattern
- factory method
- Observer Pattern
- 함수형프로그래밍
- 프로토타입 패턴
- define
- 디자인패턴
- kmp
- 추상 팩토리
- designPattern
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
8장 Job과 자식 코루틴 기다리기. 본문
구조화된 동시성 (부모-자식)
- 자식은 부모로부터 CoroutineContext를 상속받는다.
- 부모는 모든 자식이 작업을 마무리 지을때까지 기다립니다.
- 부모 Coroutine이 취소되면 자식 Coroutine도 취소된다.
- 자식 Coroutine에서 에러가 발생하면 부모 자식 Coroutine도 에러로 소멸한다.
Job
- Coroutine을 취소하고, 상태를 파악하는 등 다양하게 활용된다.
Job이란 무엇인가?
수명을 가지고 있으며 취소 가능하다

Active
- job이 실행되고, 코루틴은 잡을 수행한다. job이 코루틴 빌더에 의해 생성되었을 때, 코루틴의 본체가 실행되는 상태
- 자식 코루틴을 시작 시킬수 있다.
New
- 지연 시작되는 코루틴의 시작 상태.
- 실행하면, Active 상태가 됨
Completing
- 실행을 완료
- 자식 코루틴들을 기다리는 상태.
Completed
- 최종상태
*Active, Completing 상태에서 취소 및 실패하면, Cancelling 상태가 된다.
Cancelling
- 연결해제 및 자원반납등 후처리 작업을 수행.
Canceled
- 최종상태
모든 job은 생성과 즉시 Active상태가 된다.
import kotlinx.coroutines.*
suspend fun main() = coroutineScope {
// 빌더로 생성된 Job은
val job = Job()
println(job) // JobImpl{Active}@58651fd0
// 메서드를 완료시킬 때까지 Completed 상태다
job.complete()
println(job) // JobImpl{Completed}@58651fd0
// launch는 기본적으로 활성화되어 있습니다.
val activeJob = launch {
delay(1000)
}
println(activeJob) // StandaloneCoroutine{Active}@49fc609f
// 여기서 Job의 완료를 기다린다
activeJob.join() // (1초 후)
println(activeJob) // StandaloneCoroutine{Completed}@49fc609f
// launch는 New 상태로 지연 시작된다
val lazyJob = launch(start = CoroutineStart.LAZY) {
delay(1000)
}
println(lazyJob) // LazyStandaloneCoroutine{New}@51b7e6e0
// Active 상태가 되려면 시작하는 함수를 호출해야 한다
lazyJob.start()
println(lazyJob) // LazyStandaloneCoroutine{Active}@51b7e6e0
lazyJob.join() // (1초 후)
println(lazyJob) // LazyStandaloneCoroutine{Completed}@51b7e6e0
}
코루틴 빌더는 부모의 잡을 기초로 자신의 잡을 생성한다.
코틀린 코루틴 라이브러리는 모든 코루틴 빌더는 자신만의 잡을 생성한다.
launch 명시적 반환 타입이 job이다.
async Deferred<T> 지만, Deferred는 job 인터페이스를 구현한다.
import kotlinx.coroutines.*
suspend fun main() = coroutineScope {
val job1: Job = launch {
delay(1000L)
println("test")
}
val deferred: Deferred<String> = async {
delay(1000L)
"test"
}
val job2: Job = deferred
}
job은 CoroutineContext이기 때문에 coroutineContext[Job]으로도 접근 가능하다. 접근의 편의성을 위해 확장 프로퍼티를 사용한다.
// 확장 프로퍼티
val CoroutineContext.job: Job
get() = get(Job) ?: error("current context doesn't have job context")
fun main(): Unit = runBlocking {
println(coroutineContext.job.isActive)
}
Job은 코루틴이 상속하지 않는 유일한 coroutinecontext이며, 매우 중요한 법칙이다.
모든 코루틴은 자신만의 Job을 생성하며, 부모 코루틴으로 부터온 job은 새로운 잡의 부모로 사용이된다
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
val name = CoroutineName("some name")
val job = Job()
launch(name + job) {
val childName = coroutineContext[CoroutineName]
println(childName == name) // true
val childJob = coroutineContext[Job]
println(childJob == job) // false
println(childJob == job.children.firstOrNull()) // true
}
}
부모 잡은 자식잡 모두를 참조할 수 있고, 자식도 부모를 참수할 수 있다. job을 참조할 수 있는 부모-자식 관계가 있기에 코루틴 스코프내에서, 취소와 예외처리 구현이 가능하다.
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
val job: Job = launch {
delay(1000L)
}
val parentJob: Job = coroutineContext.job
println(job == parentJob) // false
val parentChildren: Sequence<Job> = parentJob.children
println(parentChildren.first() == job) //true
}
새로운 Job Context가 부모 잡을 대체하면, 구조화된 동시성의 작동방식은 더 이상 유효하지 않다.
fun main(): Unit = runBlocking {
launch(Job()) {
delay(1000)
println("Will not be printed")
}
}
자식은 인자로 들어온 Job을 부모로 사용하기 때문에 runBlocking의 job과 관계가 없어 기다리지 않고 종료된다.
자식들 기다리기
join 지정한 job이 최종상태(completed, canceled) 마지막 상태에 도달할 때까지 기다리는 중단한수이다.
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
launch {
delay(1000)
println("test1")
}
launch {
delay(2000)
println("test2")
}
val children = coroutineContext[Job]?.children
val childrenNum = children?.count()
println("Number of children: $childrenNum")
children?.forEach { it.join() }
println("All tests are done")
}
/*
Number of children: 2
test1
test2
All tests are done
*/
Job 팩토리 함수
팩토리 함수로 생성한 job
- 어떤 코루틴과도 연관되어 있지 않다.
- 컨텍스트로 사용할수 있다.(즉, 한 개 이상의 자식을 가진 부모 잡으로 사용될 수 있다.)
* 흔한 실수중 하나는 Job() 팩토리 함수로 잡을 생성하고 다른 코루틴의 부모로 지정한뒤에 join을 호출하는 것
모든 작업이 종료되어도 job은 acitve 상태라서 프로그램이 종료되지 않습니다.
import kotlinx.coroutines.*
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job) {
delay(1000)
println("Text 1")
}
launch(job) {
delay(2000)
println("Text 2")
}
job.join() // active 상태로 영원히 대기한다.
println("Will not be printed")
}
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job) {
delay(1000)
println("Text 1")
}
launch(job) {
delay(2000)
println("Text 2")
}
job.children.forEach { it.join() }
// 자식들만 완료가 되면 팩토리로 생성한 job은 active 상태라도 무시하고 바로 종료됨(coroutineScope 사이에는 부모-자식 관계가 형성되지 않기 때문).
}
잡팩토리 함수
@Suppress("FunctionName")
public fun Job(parent: Job? = null): CompletableJob
CompletableJob
complete(): Boolean
- 잡을 완료하는데 사용됨.
- 모든 자식 코루틴은 작업이 완료될 때까지 실행된 상태를 유지한다.
- complete 호출한 잡은 새로운 코루틴을 시작할순 없다.
import kotlinx.coroutines.*
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job) {
repeat(5) { num ->
delay(200)
println("repeat: $num")
}
}
launch {
delay(500)
job.complete()
}
job.join()
// job 이미 완료되서 시작되지 않음.
launch(job) {
println("Will not be printed")
}
println("Done!")
}
/*
repeat: 0
repeat: 1
repeat: 2
repeat: 3
repeat: 4
Done!
*/
completeExceptionally(exception: Throwable): Boolean
- 인자로 받은 예외로 잡을 완료시킨다.
- 모든 자식은 주어진 예외를 랩핑한 CancellationException으로 즉시취소 된다.
- 잡이 메서드의 실행으로 종료되었습니까?를 반환한다.
import kotlinx.coroutines.*
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job) {
repeat(5) { num ->
delay(200)
println("repeat: $num")
}
}
launch {
delay(500)
job.completeExceptionally(Error("some Error"))
}
job.join()
// job 이미 완료되서 시작되지 않음.
launch(job) {
println("Will not be printed")
}
println("Done!")
}
complete 함수는 잡의 마지막 코루틴을 시작한 후, 자주 사용되며, join을 통해 완료되는걸 기다리는 형태로 자주 쓰인다.
import kotlinx.coroutines.*
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job) {
delay(1000)
println("Text 1")
}
launch(job) {
delay(2000)
println("Text 2")
}
job.complete()
job.join()
}
Job 팩토리 함수의 인자로 부모 잡의 참조값을 전달할 수 있다.
부모 잡이 취소되면, 해당 잡 또한 취소된다.
import kotlinx.coroutines.*
suspend fun main(): Unit = coroutineScope {
val parentJob = Job()
val job = Job(parentJob)
launch(job) {
delay(1000)
println("Text 1")
}
launch(job) {
delay(2000)
println("Text 2")
}
delay(1100)
parentJob.cancel() // 부모 잡 취소
job.children.forEach { it.join() }
}'Coroutine > 코틀린 코루틴' 카테고리의 다른 글
| 7장 코루틴 컨텍스트 (1) | 2026.01.19 |
|---|---|
| 6장 코루틴 빌더 (0) | 2026.01.05 |
| 5장 코루틴 언어차원에서의 지원 vs 라이브러리 (0) | 2025.12.22 |
| 3장 중단은 어떻게 작동할까? (0) | 2025.11.27 |
| 2장 시퀀스 빌더 (0) | 2025.11.17 |
