[Coroutine] μ½”λ£¨ν‹΄μ˜ μ‹€μ œ κ΅¬ν˜„

 πŸ’‘ μ‹€μ œ 코루틴 λ‚΄λΆ€κ°€ μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ μ΄ν•΄ν•˜κΈ° μœ„ν•œ ν•™μŠ΅μž…λ‹ˆλ‹€.

 

μ½”λ£¨ν‹΄μ˜ μ‹€μ œ κ΅¬ν˜„?

  • 쀑단 ν•¨μˆ˜λŠ” ν•¨μˆ˜κ°€ μ‹œμž‘ν•  λ•Œμ™€ 쀑단 ν•¨μˆ˜κ°€ ν˜ΈμΆœλ˜μ—ˆμ„ λ•Œ μƒνƒœλ₯Ό κ°€μ§„λ‹€λŠ” μ μ—μ„œ μƒνƒœ λ¨Έμ‹ (state machine)κ³Ό λΉ„μŠ·ν•©λ‹ˆλ‹€.
    • *state machine : ν•˜λ‚˜μ˜ μƒνƒœλ§Œμ„ κ°€μ§€λŠ” 좔상 기계
  • μ»¨ν‹°λ‰΄μ—μ΄μ…˜(continuation) κ°μ²΄λŠ” μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄λŠ” μˆ«μžμ™€ 둜컬 데이터λ₯Ό 가지고 μžˆμŠ΅λ‹ˆλ‹€.
    • ν•¨μˆ˜μ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체가 이 ν•¨μˆ˜λ₯Ό λΆ€λ₯΄λŠ” λ‹€λ₯Έ ν•¨μˆ˜μ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체λ₯Ό μž₯식(Decorate)이라고 ν•©λ‹ˆλ‹€.
    • μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λŠ” μ‹€ν–‰μ„ μž¬κ°œν•˜κ±°λ‚˜ 재개된 ν•¨μˆ˜λ₯Ό μ™„λ£Œν•  λ•Œ μ‚¬μš©λ˜λŠ” 콜 μŠ€νƒμœΌλ‘œ μ‚¬μš©λ©λ‹ˆλ‹€.

μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 전달 방식 (CPS)

  • continuation-passing style
  • ν”„λ‘œκ·Έλž¨μ˜ 흐름을 μ œμ–΄ν•˜κΈ° μœ„ν•΄ continuationλ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ „λ‹¬ν•˜λŠ” μŠ€νƒ€μΌμ΄λ©°, ν”„λ‘œκ·Έλž¨μ˜ ν˜„μž¬ μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄λŠ” 객체인 continuation을 λ‹€μŒ ν•¨μˆ˜ ν˜ΈμΆœμ— μ „λ‹¬ν•˜μ—¬ μ œμ–΄ 흐름을 μœ μ§€ν•©λ‹ˆλ‹€.
  • ν•¨μˆ˜μ—μ„œ ν•¨μˆ˜λ‘œ 인자λ₯Ό 톡해 μ „λ‹¬λ˜λ©°, λ§ˆμ§€λ§‰ νŒŒλΌλ―Ένƒ€λ‘œ μ „λ‹¬λ©λ‹ˆλ‹€.
suspend fun getUser() : User?

// λ‚΄λΆ€λŠ”..
fun getUser(continuation: Continuation<*>): Any
  • Nullable ν˜•νƒœλ‘œ 바뀐 μ΄μœ λŠ”, 쀑단 ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜λŠ” 도쀑에 μ€‘λ‹¨λ˜λ©΄ μ„ μ–Έλœ νƒ€μž…μ˜ 값을 λ°˜ν™˜ν•˜μ§€ μ•Šμ„ μˆ˜λ„ 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.
  • 각 ν•¨μˆ˜λŠ” μžμ‹ λ§Œμ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체λ₯Ό ν•„μš”λ‘œ ν•©λ‹ˆλ‹€.

일반적인 비동기 μž‘μ—… with asyncTask

fun main() {
    println("Before")
    asyncTask {
        println("After")
    }
}

fun asyncTask(callback: () -> Unit) {
    Thread.sleep(1000) // delay
    callback()
}
  • μœ„ μ½”λ“œμ—μ„œλŠ” asyncTaskκ°€ μ™„λ£Œλœ 후에 μ½œλ°±μ„ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • ν•΄λ‹Ή μž‘μ—…μ„ CPS(continuation-passing style)둜 μˆ˜μ •ν•˜λ©΄ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.
fun main() {
    println("Before")
    asyncTask(::afterTask)
}

fun asyncTask(continuation: () -> Unit) {
    Thread.sleep(1000) // delay
    continuation()
}

fun afterTask() {
    println("After")
}
  • μ—¬κΈ°μ„œ asyncTaskλŠ” μž‘μ—…μ΄ μ™„λ£Œλœ ν›„ ν˜ΈμΆœν•  continuation을 인수둜 λ°›μœΌλ©°, continuationλŠ” μž‘μ—…μ΄ μ™„λ£Œλ˜λ©΄ ν˜ΈμΆœλ©λ‹ˆλ‹€.

μ½”ν‹€λ¦° μ½”λ£¨ν‹΄μ—μ„œλŠ”?

  • μ½”ν‹€λ¦° μ½”λ£¨ν‹΄μ—μ„œλŠ” 이 κ°œλ…μ΄ 쀑단을 μž¬κ°œν•  수 있게 ν•˜λŠ” λ‚΄λΆ€ λ©”μ»€λ‹ˆμ¦˜μœΌλ‘œ μ‚¬μš©λ©λ‹ˆλ‹€.
  • suspend ν•¨μˆ˜λ₯Ό ν™œμš©ν•˜μ—¬ λ‚΄λΆ€μ μœΌλ‘œ continuation을 μ‚¬μš©ν•˜μ—¬ μ½”λ£¨ν‹΄μ˜ 쀑단 지점을 κ΄€λ¦¬ν•©λ‹ˆλ‹€.
fun main() {
    val continuation = suspendAndSetContinuation()
    continuation.resume(Unit)
    println("After resuming")
}

suspend fun suspendAndSetContinuation(): Continuation<Unit> {
    return suspendCoroutine { cont ->
        println("Before suspending")
        cont // Return the continuation to the caller
    }
}

μƒνƒœλ₯Ό 가진 ν•¨μˆ˜

  • ν•¨μˆ˜κ°€ μ€‘λ‹¨λœ 후에 λ‹€μ‹œ μ‚¬μš©ν•  지역 λ³€μˆ˜λ‚˜ νŒŒλΌλ―Έν„°μ™€ 같은 μƒνƒœλ₯Ό 가지고 μžˆλ‹€λ©΄, ν•¨μˆ˜μ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체에 μƒνƒœλ₯Ό μ €μž₯ν•΄μ•Ό ν•©λ‹ˆλ‹€.
suspend fun myFunction() {
    println("Before")
    var counter = 0
    delay(1000) //쀑단 ν•¨μˆ˜
    counter++
    println("Counter: $counter")
    println("After")
}
  • counterλŠ” 0κ³Ό 1둜 ν‘œμ‹œλœ 두 μƒνƒœλ₯Ό κ°€μ§€κ²Œ λ©λ‹ˆλ‹€.
  • 이 λ•Œ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체λ₯Ό ν†΅ν•΄μ„œ 이λ₯Ό μ „λ‹¬ν•΄μ•Όν•˜λ©°, μ€‘λ‹¨λ˜κΈ° 전에 값을 μ €μž₯ν•˜κ³  재개될 λ•Œ λ³΅κ΅¬λ©λ‹ˆλ‹€.

값을 λ°›μ•„ μž¬κ°œλ˜λŠ” ν•¨μˆ˜

  • 쀑단 ν•¨μˆ˜λ‘œλΆ€ν„° 값을 λ°›μ•„μ•Όν•˜λŠ” 경우λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
suspend fun printUser(token: String) {
    println("Before")
    val userId = getUserId(token) // 쀑단 ν•¨μˆ˜
    println("Got UserId: $userId")
    val userName = getUserName(userId, token) //쀑단 ν•¨μˆ˜
    println(User(userId,userName))
    println("After")
}
  • 두 가지 쀑단 ν•¨μˆ˜κ°€ 있으며 μ•„λž˜μ™€ 같은 μ œμ•½μ‚¬ν•­μ΄ μžˆμŠ΅λ‹ˆλ‹€.
    • tokenμ΄λΌλŠ” νŒŒλΌλ―Έν„°λ₯Ό λ°›μœΌλ©΄ 쀑단 ν•¨μˆ˜λŠ” νŠΉμ • 값을 λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€.
    • νŒŒλΌλ―Έν„°μ™€ λ°˜ν™˜κ°’ λͺ¨λ‘ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체에 μ €μž₯λ˜μ–΄μ•Ό ν•œλ‹€.
  • ν•¨μˆ˜κ°€ κ°’μœΌλ‘œ μž¬κ°œλ˜μ—ˆλ‹€λ©΄ κ²°κ³ΌλŠ” Result.Success(value)κ°€ λ©λ‹ˆλ‹€.
  • λ°˜λ©΄μ— μ˜ˆμ™Έλ‘œ μž¬κ°œλ˜μ—ˆλ‹€λ©΄ κ²°κ³ΌλŠ” Result.Failure(exception)이 되며 μ˜ˆμ™Έλ₯Ό λ˜μ§‘λ‹ˆλ‹€.

콜 μŠ€νƒ(call stack)

  • ν•¨μˆ˜ aκ°€ ν•¨μˆ˜ bλ₯Ό ν˜ΈμΆœν•˜λ©΄ κ°€μƒ 머신은 a의 μƒνƒœμ™€ bκ°€ λλ‚˜λ©΄ 싀행이 될 지점을 μ–΄λ”˜κ°€μ— μ €μž₯ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • 이λ₯Ό μ½”λ£¨ν‹΄μ—μ„œλŠ” 콜 μŠ€νƒ(call stack)μ΄λΌλŠ” μžλ£Œκ΅¬μ‘°μ— μ €μž₯ν•˜λ©°, μ½”루틴을 μ€‘λ‹¨ν•˜λ©΄ μŠ€λ ˆλ“œλ₯Ό λ°˜ν™˜ν•΄ 콜 μŠ€νƒμ— μžˆλŠ” 정보λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€.
    • 코루틴을 μž¬κ°œν•  λ•Œ 콜 μŠ€νƒμ„ μ‚¬μš©ν•  μˆ˜μ—†μœΌλ―€λ‘œ, μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체가 콜 μŠ€νƒμ˜ 역할을 λŒ€μ‹ ν•©λ‹ˆλ‹€.
  • λ”°λΌμ„œ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λŠ” μ•„λž˜μ™€ 같은 정보λ₯Ό κ°€μ§‘λ‹ˆλ‹€.
    • 쀑단 λ˜μ—ˆμ„ λ•Œμ˜ μƒνƒœ(label)
    • 지역 λ³€μˆ˜μ™€ νŒŒλΌλ―Έν„°(ν•„λ“œ)
    • 쀑단 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ ν•¨μˆ˜κ°€ 재개될 μœ„μΉ˜ 정보

μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체의 μ°Έμ‘°

  • ν•˜λ‚˜μ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체가 λ‹€λ₯Έ ν•˜λ‚˜λ₯Ό μ°Έμ‘°ν•˜κ³ , 참쑰된 객체가 λ˜λ‹€λ₯Έ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체λ₯Ό μ°Έμ‘°ν•©λ‹ˆλ‹€.
  • 이 λ•Œλ¬Έμ— μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λŠ” κ±°λŒ€ν•œ μ–‘νŒŒμ™€ κ°™μœΌλ©°, 일반적으둜 콜 μŠ€νƒμ— μ €μž₯λ˜λŠ” 정보λ₯Ό λͺ¨λ‘ 가지고 μžˆμŠ΅λ‹ˆλ‹€.

μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체의 λ™μž‘ μˆœμ„œ

  1. μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체가 재개될 λ•Œ 각 μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λŠ” μžμ‹ μ΄ λ‹΄λ‹Ήν•˜λŠ” ν•¨μˆ˜λ₯Ό λ¨Όμ € ν˜ΈμΆœν•©λ‹ˆλ‹€.
  2. ν•¨μˆ˜μ˜ 싀행이 λλ‚˜λ©΄ μžμ‹ μ΄ ν˜ΈμΆœν•œ ν•¨μˆ˜μ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜μ„ μž¬κ°œν•©λ‹ˆλ‹€.
  3. 재개된 μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체 λ˜ν•œ λ‹΄λ‹Ήν•˜λŠ” ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  4. 이 과정을 μŠ€νƒμ˜ 끝에 λ‹€λ‹€λ₯Ό λ•ŒκΉŒμ§€ λ°˜λ³΅ν•©λ‹ˆλ‹€.
override fun resumeWith(result: Result<String>) {
    this.result = result
    val res = try {
        val r = printUser(token,this)
        if (r == COROUTINE_SUSPENDED) return
        Result.success(r as Unit)
    } catch(e: Throwable) {
        Result.failure(e)
    }
    completion.resumeWith(res)
}
  • ν•¨μˆ˜ aκ°€ ν•¨μˆ˜ bλ₯Ό ν˜ΈμΆœν•˜κ³ , ν•¨μˆ˜ bλŠ” ν•¨μˆ˜ cλ₯Ό ν˜ΈμΆœν•  λ•Œ, ν•¨μˆ˜ cμ—μ„œ μ€‘λ‹¨λœ 상황을 ν™•μΈν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • 싀행이 재개되면 c의 μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λŠ” c ν•¨μˆ˜λ₯Ό λ¨Όμ € μž¬κ°œν•©λ‹ˆλ‹€.

  • a() → b() → c()μ—μ„œ μ€‘λ‹¨λœλ‹€λ©΄, c() 재개 → b() 재개 → a() 재개 순으둜 λ™μž‘ν•©λ‹ˆλ‹€.

μ˜ˆμ™Έ μƒν™©μ—μ„œλŠ”?

  • μ˜ˆμ™Έλ₯Ό 던질 λ•Œλ„ 이와 λΉ„μŠ·ν•˜κ²Œ λ™μž‘ν•©λ‹ˆλ‹€.
  • resumeWithμ—μ„œ 작히면 Result.failure(e)둜 λž˜ν•‘λ˜λ©°, μ˜ˆμ™Έλ₯Ό λ˜μ§„ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ ν•¨μˆ˜λŠ” 포μž₯된 κ²°κ³Όλ₯Ό 받을 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ€‘λ‹¨λœ ν•¨μˆ˜κ°€ μž¬κ°œν–ˆμ„ λ•Œ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λ‘œλΆ€ν„° μƒνƒœλ₯Ό λ³΅μ›ν•˜κ³ , 얻은 κ²°κ³Όλ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ μ˜ˆμ™Έλ₯Ό λ˜μ Έμ•Ό ν•©λ‹ˆλ‹€.

μ‹€μ œ 코루틴 μ½”λ“œ

  • μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체와 쀑단 ν•¨μˆ˜λ₯Ό μ»΄νŒŒμΌν•œ μ‹€μ œ μ½”λ“œλŠ” μ΅œμ ν™”λ˜μ–΄ 있으며, μ•„λž˜μ™€ 같은 λͺ‡ 가지 처리 과정이 더 ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
    • μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ•Œ 더 λ‚˜μ€ μŠ€νƒ 트레이슀 생성
    • 코루틴 쀑단 μΈν„°μ…‰μ…˜
    • μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ³€μˆ˜λ₯Ό μ œκ±°ν•˜κ±°λ‚˜, ν…ŒμΌμ½œ 졜적
    • tail-call : ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ 값을 λ°˜ν™˜λ°›μ€ λ’€ μ–΄λ– ν•œ ν›„μ²˜λ¦¬ 없이 κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•˜λŠ” 방식
    • tail-call optimization : ν…ŒμΌμ½œλ‘œ μ§œμ—¬μ§„ μ½”λ“œμ—μ„œ ν…Œμ΄μ½œλ‘œ ν˜ΈμΆœν•˜λŠ” ν•¨μˆ˜μ— λŒ€ν•œ μŠ€νƒμ„ λ§Œλ“€μ§€ μ•Šκ³ , ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ 값을 λŒ€μ‹  μ‚¬μš©ν•˜μ—¬ μŠ€νƒμ„ μ΅œμ†Œν•œμœΌλ‘œ λ§Œλ“œλŠ” μ΅œμ ν™” 방식

쀑단 ν•¨μˆ˜μ˜ μ„±λŠ₯

  • 코루틴 λ‚΄λΆ€ κ΅¬ν˜„μ„ λ³Έλ‹€λ©΄ λŒ€λΆ€λΆ„μ˜ μ‚¬λžŒλ“€μ΄ λΉ„μš©μ΄ 클 것이라고 μƒκ°ν•˜μ§€λ§Œ, μ‹€μ œλ‘œλŠ” 그렇지 μ•ŠμŠ΅λ‹ˆλ‹€ !
  • ν•¨μˆ˜λ₯Ό μƒνƒœλ‘œ λ‚˜λˆ„λŠ” 것은 λΉ„μš©μ΄ 거의 μ—†μœΌλ©°, κ°„λ‹¨ν•œ 방식을 ν™œμš©ν•©λ‹ˆλ‹€.
  • Rxjavaλ‚˜ 콜백 ν•¨μˆ˜μ˜ μ„±λŠ₯κ³Ό λΉ„κ΅ν–ˆμ„ λ•Œ 큰 λΉ„μš©μ„ μš”κ΅¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μš”μ•½

  • 쀑단 ν•¨μˆ˜λŠ” μƒνƒœ λ¨Έμ‹ κ³Ό λΉ„μŠ·ν•˜λ©°, ν•¨μˆ˜κ°€ μ‹œμž‘λ  λ•Œμ™€ 쀑단 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ λ’€μ˜ μƒνƒœλ₯Ό κ°€μ§‘λ‹ˆλ‹€.
  • μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄λŠ” κ°’κ³Ό 둜컬 λ°μ΄ν„°λŠ” μ»¨ν‹°λ‰΄μ—μ΄μ…˜ 객체에 μ €μž₯λ©λ‹ˆλ‹€.
  • ν˜ΈμΆœν•œ ν•¨μˆ˜μ˜ μ»¨ν‹°λ‰΄μ—μ΄μ…˜ κ°μ²΄λŠ” ν•¨μˆ˜κ°€ 재개될 λ•Œ λ˜λŠ” 재개된 ν•¨μˆ˜κ°€ μ™„λ£Œλ  λ•Œ μ‚¬μš©λ˜λŠ” 콜 μŠ€νƒμ˜ 역할을 ν•©λ‹ˆλ‹€.

https://m.yes24.com/Goods/Detail/123034354

 

μ½”ν‹€λ¦° 코루틴 - 예슀24

μ½”ν‹€λ¦° μ „λ¬Έ 강사가 μ•Œλ € μ£ΌλŠ” μ½”ν‹€λ¦° 코루틴에 λŒ€ν•œ λͺ¨λ“  것!μ½”ν‹€λ¦° 코루틴은 효율적이고 μ‹ λ’°ν•  수 μžˆλŠ” λ©€ν‹°μŠ€λ ˆλ“œ ν”„λ‘œκ·Έλž¨μ„ μ‰½κ²Œ κ΅¬ν˜„ν•  수 있게 ν•΄ μ£Όμ–΄ μžλ°” 가상 λ¨Έμ‹ (JVM), 특히 μ•ˆλ“œλ‘œ

m.yes24.com