[Coroutine] Jobκ³Ό Child 코루틴

πŸ’‘ κ΅¬μ‘°ν™”λœ λ™μ‹œμ„±μ— μ΄μ–΄μ„œ Jobκ³Ό child μ½”λ£¨ν‹΄μ˜ νŠΉμ§•μ— λŒ€ν•˜μ—¬ ν•™μŠ΅ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

Coroutine Job?

  • Job은 코루틴을 μ·¨μ†Œν•˜κ³ , μƒνƒœλ₯Ό νŒŒμ•…ν•˜λŠ” λ“± λ‹€μ–‘ν•˜κ²Œ μ‚¬μš©λ  수 μžˆλŠ” μ»¨ν…μŠ€νŠΈμž…λ‹ˆλ‹€.
  • 수λͺ…μ£ΌκΈ°λ₯Ό 가지며 μΈν„°νŽ˜μ΄μŠ€ μž…λ‹ˆλ‹€.
  • ꡬ체적인 μ‚¬μš©λ²•κ³Ό μƒνƒœλ₯Ό 가지고 μžˆλ‹€λŠ” μ μ—μ„œ 좔상 클래슀처럼 λ‹€λ£° 수 μžˆμŠ΅λ‹ˆλ‹€.

Coroutine Job μƒνƒœ 도식도

  • μ•„λž˜μ™€ 같이 6가지 μƒνƒœμ™€ λ„μ‹ν‘œλ‘œ λ‚˜νƒ€λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ‹€μ œ μ½”λ“œμ—μ„œλŠ” toString을 ν™œμš©ν•΄ 작의 μƒνƒœλ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

  • Active
    • 작이 μ‹€ν–‰λ˜κ³  코루틴이 μž‘μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
    • 작이 코루틴 λΉŒλ”μ— μ˜ν•΄ μƒμ„±λ˜μ—ˆμ„ λ•Œ, 본체가 μ‹€ν–‰λ˜λŠ” μƒνƒœμ΄λ©° μžμ‹ 코루틴을 μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • New
    • λŒ€λΆ€λΆ„ 코루틴은 Active μƒνƒœλ‘œ μ‹œμž‘λ˜μ§€λ§Œ, 지연 μ‹œμž‘λ˜λŠ” 코루틴은 New μƒνƒœλ‘œ μ‹œμž‘λ©λ‹ˆλ‹€.
    • New → Active둜 μ „ν™˜ν•˜λ €λ©΄ μž‘μ—…μ΄ μ‹€ν–‰λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
  • Completing
    • μž‘μ—…μ˜ 싀행이 μ™„λ£Œλ˜λ©΄ λ³€ν™”ν•˜λŠ” μƒνƒœμž…λ‹ˆλ‹€.
  • Completed
    • μžμ‹λ“€μ˜ 싀행이 λͺ¨λ‘ λλ‚œ μƒνƒœμž…λ‹ˆλ‹€.
  • Cancelling
    • 작이 μ‹€ν–‰ 도쀑에 μ·¨μ†Œλ˜κ±°λ‚˜ μ‹€νŒ¨ν•˜κ²Œ λ˜λŠ” μƒνƒœμž…λ‹ˆλ‹€.
    • μ—¬κΈ°μ„œμ˜ 도쀑은 New μƒνƒœλŠ” ν•΄λ‹Ήν•˜μ§€ μ•ŠμœΌλ©°, Activeλ‚˜ Complet-ing μƒνƒœμ—μ„œλ§Œ 올 수 μžˆμŠ΅λ‹ˆλ‹€.
    • 연결을 λŠκ±°λ‚˜ μžμ›μ„ λ°˜λ‚©ν•˜λŠ” λ“±μ˜ ν›„μ²˜λ¦¬ μž‘μ—…μ„ 진행할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • Cancelled
    • ν›„μ²˜λ¦¬ μž‘μ—…κΉŒμ§€ μ™„λ£Œλ˜λ©΄ λ„λ‹¬ν•˜λŠ” μ΅œμ’… μƒνƒœμž…λ‹ˆλ‹€.

코루틴 λΉŒλ”μ™€ 작의 관계

  • μ½”ν‹€λ¦° 코루틴 라이브러리의 λͺ¨λ“  코루틴 λΉŒλ”λŠ” μžμ‹ λ§Œμ˜ μž‘μ„ μƒμ„±ν•©λ‹ˆλ‹€.
  • λŒ€λΆ€λΆ„μ˜ 코루틴 λΉŒλ”λŠ” μž‘μ„ λ°˜ν™˜ν•˜λ©°, μ–΄λŠ κ³³μ—μ„œλ“  μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
fun main(): Unit = runBlocking {
    val job : Job = lauch {
        delay(1000)
        prinlnt("Test")
    }
    val deferred : Deferred<String> = async {
        delay(1000)
        "Test"
    }
    val job : Job = deferred
}
  • launch의 λͺ…μ‹œμ  λ°˜ν™˜ νƒ€μž…μ€ Job이며, async ν•¨μˆ˜μ— μ˜ν•΄ λ°˜ν™˜λ˜λŠ” ν•¨μˆ˜λŠ” Defferred<T>둜 이 λ˜ν•œ Job μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • λ˜ν•œ Job은 코루틴 μ»¨ν…μŠ€νŠΈμ΄λ―€λ‘œ coroutineContext[Job]을 μ‚¬μš©ν•΄ μ ‘κ·Όν•˜λŠ” 것도 κ°€λŠ₯ν•©λ‹ˆλ‹€.
    • μ΄λŠ” ν™•μž₯ ν”„λ‘œνΌν‹°λ₯Ό ν™œμš©ν•˜λŠ” 것도 κ°€λŠ₯ν•©λ‹ˆλ‹€.
val CoroutineContext.job: Job
    get() = get(Job) ?: error("Current context donsn't")

fun main(): Unit = runBlocking {
    print(coroutineContext.job.isActive) // true
}
  • μž‘μ€ λΆ€λͺ¨ - μžμ‹ 관계가 있기 λ•Œλ¬Έμ— 코루틴 μŠ€μ½”ν”„ λ‚΄μ—μ„œ μ·¨μ†Œμ™€ μ˜ˆμ™Έ 처리 κ΅¬ν˜„μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.
fun main(): Unit = runBlocking {
    val job: Job = launch { // μžλ™μœΌλ‘œ λΆ€λͺ¨ μž‘μ„ μ‚¬μš©
        delay(1000)
    }

    val parentJob: Job = coroutineContext.job // coroutineContext[Job]
    println(job == parentJob) // false
    val parentChildren: Sequence<Job> = parentJob.children
    println(parentChildren.first() == job) // true 
}
  • μƒˆλ‘œμš΄ Job μ»¨ν…μŠ€νŠΈκ°€ λΆ€λͺ¨μ˜ μž‘μ„ λŒ€μ²΄ν•˜λ©΄ κ΅¬μ‘°ν™”λœ λ™μ‹œμ„±μ΄ μœ νš¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
fun main(): Unit = runBlocking {
    launch(Job()) { // λΆ€λͺ¨μž‘을 μ‚¬μš©ν•˜μ§€ μ•Šκ³  μƒˆλ‘œ 생성
        delay(1000)
        println("print?")
    }
}
// 아무것도 좜λ ₯λ˜μ§€ μ•Šκ³  μ’…λ£Œλ©λ‹ˆλ‹€.
  • μœ„ μ½”λ“œμ—μ„œλŠ” λΆ€λͺ¨ μžμ‹ 사이에 μ•„λ¬΄λŸ° 관계가 μ—†κΈ° λ•Œλ¬Έμ— λΆ€λͺ¨κ°€ μžμ‹ 코루틴을 기닀리지 μ•ŠμŠ΅λ‹ˆλ‹€.
  • μžμ‹μ„ 인자둜 λ“€μ–΄μ˜¨ μž‘μ„ λΆ€λͺ¨λ‘œ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— runBlockingκ³Ό 아무련 관련이 μ—†κ²Œ λ©λ‹ˆλ‹€.
  • μ½”νˆ¬ν‹΄μ΄ μžμ‹ λ§Œμ˜ λ…μžμ μΈ μž‘μ„ 가지고 있으면 λΆ€λͺ¨μ™€ 아무련 관련이 μ—†μœΌλ©°, κ΅¬μ‘°ν™”λœ λ™μ‹œμ„±μ„ μžƒκ²Œ λ©λ‹ˆλ‹€.

μžμ‹λ“€ 기닀리기

  • 작의 κ°€μž₯ μ€‘μš”ν•œ 이점은 코루틴이 μ™„λ£Œλ  λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬λŠ” 데 μ‚¬μš©ν•  수 μžˆλ‹€λŠ” μ μž…λ‹ˆλ‹€.
  • 이λ₯Ό μœ„ν•΄μ„œ join λ©”μ„œλ“œλ₯Ό ν™œμš©ν•©λ‹ˆλ‹€.
  • join은 μ§€μ •ν•œ 작이 Completedλ‚˜ Cancelled와 같은 λ§ˆμ§€λ§‰ μƒνƒœμ— 도달할 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬λŠ” 쀑단 ν•¨μˆ˜μž…λ‹ˆλ‹€.
fun main(): Unit = runBlocking {
    val job1 = launch {
        delay(1000)
        print("Test1")
    }
    val job2 = lauch {
        delay(1000)
        print("Test2")
    }

    job1.join()
    job2.join()
    print("all done")
}
// 1초 ν›„ .. Test1
// 1초 ν›„ .. Test2
// all done
  • Job μΈν„°νŽ˜μ΄μŠ€λŠ” λͺ¨λ“  μžμ‹μ„ μ°Έμ‘°ν•  수 μžˆλŠ” children ν”„λ‘œνΌν‹°λ₯Ό λ…ΈμΆœμ‹œν‚΅λ‹ˆλ‹€.
    • 이λ₯Ό ν™œμš©ν•΄μ„œ λͺ¨λ“  μžμ‹μ΄ λ§ˆμ§€λ§‰ μƒνƒœκ°€ 될 λ•ŒκΉŒμ§€ 기닀릴 수 μžˆμŠ΅λ‹ˆλ‹€.
val children = coroutineContext[Job]?.children

작 νŒ©ν† λ¦¬ ν•¨μˆ˜

  • Job() νŒ©ν† λ¦¬ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ 코루틴 없이도 Job을 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ–΄λ–€ 코루틴과도 μ—°κ΄€λ˜μ§€ μ•ŠμœΌλ©°, μ»¨ν…μŠ€νŠΈλ‘œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • μ΄λŠ” ν•œ 개 μ΄μƒμ˜ μžμ‹ 코루틴을 가진 λΆ€λͺ¨ 작으둜 μ‚¬μš©ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.
    • ν”ν•œ μ‹€μˆ˜ 쀑 ν•˜λ‚˜λŠ” Job() νŒ©ν† λ¦¬ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ μž‘μ„ μƒμ„±ν•˜κ³ , λ‹€λ₯Έ μ½”νˆ¬λ¦°μ˜ λΆ€λͺ¨λ‘œ μ§€μ •ν•œ 뒀에 join을 ν˜ΈμΆœν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.
      • μ΄λŠ” μžμ‹ 코루틴이 λͺ¨λ‘ μž‘μ—…μ„ λλ§ˆμ³λ„ Job이 μ—¬μ „νžˆ μ•‘ν‹°λΈŒ μƒνƒœμ— 있기 λ•Œλ¬Έμ— ν”„λ‘œκ·Έλž¨μ΄ μ’…λ£Œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
suspend fun main() : Unit = coroutineScope {
    val job = Job()
    launch(job) { // μƒˆλ‘œμš΄ 작이 λΆ€λͺ¨λ‘œλΆ€νˆ¬μ–΄ 상속받은 μž‘μ„ λŒ€μ²΄ 
        delay(1000)
        println("print")
    }
    launch(job) { // μƒˆλ‘œμš΄ 작이 λΆ€λͺ¨λ‘œλΆ€νˆ¬μ–΄ 상속받은 μž‘μ„ λŒ€μ²΄ 
        delay(2000)
        println("print2")
    }
    job.join()
    println("end?")
}
// 1초 ν›„ .. print
// 1초 ν›„ .. print2
// μ˜μ›νžˆ μ‹€ν–‰
  • λ”°λΌμ„œ 작의 λͺ¨λ“  μžμ‹ μ½”λ£¨ν‹΄μ—μ„œ join을 ν˜ΈμΆœν•˜λŠ” 것이 λ°”λžŒμ§ν•œ λ°©λ²•μž…λ‹ˆλ‹€.
job.children.forEach{ it.join() }

CompletableJob

  • Job의 ν•˜μœ„ μΈν„°νŽ˜μ΄μŠ€λ‘œ, μ•„λž˜μ˜ 두 가지 λ©”μ„œλ“œλ₯Ό κ°€μ§‘λ‹ˆλ‹€.
  • complete() : Boolean
    • μž‘μ„ μ™„λ£Œν•˜λŠ” 데 μ‚¬μš©ν•˜λ©°, λͺ¨λ“  μžμ‹ 코루틴은 μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ μ‹€ν–‰ μƒνƒœλ₯Ό μœ μ§€ν•˜μ§€λ§Œ, completeλ₯Ό ν˜ΈμΆœν•œ μž‘μ—μ„œ μƒˆλ‘œμš΄ 코루틴이 μ‹œμž‘λ  μˆ˜λŠ” μ—†μŠ΅λ‹ˆλ‹€.
    • 작이 μ™„λ£Œλ˜λ©΄ μ‹€ν–‰ κ²°κ³ΌλŠ” true, 그렇지 μ•Šμ„ 경우 falseκ°€ λ©λ‹ˆλ‹€.
fun main() = runBlocking {
    val job = Job()

    launch(job) {
        repeat(3) { num ->
            delay(200)
            println("Rep$num")
        }
    }

    launch {
        delay(500)
        job.complete()
    }

    job.join()

    launch(job){ // job이 μ™„λ£Œλ˜μ—ˆκΈ° λ•Œλ¬Έμ—, μƒˆλ‘œμš΄ 코루틴을 μ‹œμž‘ν•˜μ§€ μ•ŠμŒ
        println("print")
    }

    println("Done")
}

// Rep0
// Rep1
// Rep2
// Done
  • completeExceptionally(exception: Throwable): Boolean
    • 인자둜 받은 μ˜ˆμ™Έλ‘œ μž‘μ„ μ™„λ£Œμ‹œν‚΅λ‹ˆλ‹€.
    • λͺ¨λ“  μžμ‹ 코루틴은 주어진 μ˜ˆμ™Έλ₯Ό λž˜ν•‘ν•œ CancellationException으둜 μ¦‰μ‹œ μ·¨μ†Œλ©λ‹ˆλ‹€.

fun main() = runBlocking {
    val job = Job()

    launch(job) {
        repeat(3) { num ->
            delay(200)
            println("Rep$num")
        }
    }

    launch {
        delay(500)
        job.completeExceptionally(Error("error"))
    }

    job.join()

    launch(job){
        println("print")
    }

    println("Done")
}

// Rep0
// Rep1
// Done
  • 500μ΄ˆμ— completeExceptionally()λ₯Ό λ°œμƒμ‹œμΌ°μœΌλ―€λ‘œ Rep2λŠ” 좜λ ₯λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.