03
29

https://kotlinlang.org/docs/composing-suspending-functions.html

 

Composing suspending functions | Kotlin

 

kotlinlang.org

์œ„ ๋งํฌ๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉฐ ๋‚จ๊ธฐ๋Š” ๊ธฐ๋ก์ด๋‹ค.

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = doSomethingUsefulOne()
        val two = doSomethingUsefulTwo()
        println("The answer is ${one + two}")
    }
    println("Completed in $time ms")    
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}

๋ถ€๋ชจ ์ฝ”๋ฃจํ‹ด ์Šค์ฝ”ํ”„์˜ ์‹คํ–‰์‹œ๊ฐ„์€ ์ž์‹ ์Šค์ฝ”ํ”„์˜ ์‹คํ–‰์‹œ๊ฐ„๋„ ๋ชจ๋‘ ํฌํ•จํ•œ๋‹ค. measureTimeMillis๋Š” ๋‚ด๋ถ€ ๊ตฌ๋ฌธ ์‹คํ–‰์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋‹ค.

 

Async

์œ„ ์ฝ”๋“œ๋Š” ๋‘ suspend fun๊ณผ ์ฝ”๋ฃจํ‹ด ๋‚ด๋ถ€ ์ฝ”๋“œ๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ๋Œ์•„๊ฐ„๋‹ค. ๋”ฐ๋ผ์„œ ์‹คํ–‰์‹œ๊ฐ„์ด ์•ฝ 2000ms์œผ๋กœ ์žกํžŒ๋‹ค. ์ด๊ฑธ ์›ํ•˜์ง€์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ํ…๋ฐ async๋ฅผ ์‚ฌ์šฉํ•ด ์กฐ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. async๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด launch์™€ ๊ฐ™์ด ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ์— ์ž‘๋™์‹œํ‚ฌ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๊ณ  launch๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‹œ๊ธฐ๋ฅผ ์กฐ์ ˆํ•˜์ง€ ๋ชปํ•˜์ง€๋งŒ async๋Š” await()๋ฅผ ์ด์šฉํ•ด์„œ ์—ฐ์‚ฐ์‹œ๊ธฐ๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

val time = measureTimeMillis {
        val one = async { doSomethingUsefulOne() }
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
println("Completed in $time ms")

์ด ์ฝ”๋“œ์˜ ์‹คํ–‰์‹œ๊ฐ„์€ ์•ฝ 1000ms์ด๋‹ค. 

fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext, 
    start: CoroutineStart = CoroutineStart.DEFAULT, 
    block: suspend CoroutineScope.() -> T
): Deferred<T>

async์˜ ๊ตฌ์กฐ์ธ๋ฐ Deferred ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๊ฑด Job๊ณผ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•˜๋ฉฐ cancellableํ•˜๋‹ค.

 

CoroutineStart.LAZY

val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
one.start()
two.start()

async์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” start๊ฐ€ ์žˆ๋‹ค. ์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ CoroutineStart.LAZY๋กœ ์ง€์ •ํ•˜๋ฉด lazilyํ•˜๊ฒŒ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด ๊ฒฝ์šฐ ์›๋ž˜ active ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” Deferred๊ฐ€ new ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค. new ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด์ง„ ์ด Deferred(Job๊ณผ ๋™์ผํ•จ์„ ๊ธฐ์–ต)๋Š”  start, join, await๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ active ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค. active ์‹œ์ ์„ ์ง์ ‘ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ํฐ ์žฅ์ ์ด๋‹ค. by lazy๋กœ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ์ดˆ๊ธฐํ™”๋˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ดํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

 

LAZY๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์•ˆํ•˜๊ณ ์˜ ์‹คํ–‰์‹œ๊ฐ„ ์ฐจ์ด๋Š” ๊ฑฐ์˜ ์—†๋‹ค. ๊ทธ๋Ÿฌ๋‚˜  new์ƒํƒœ๋กœ ๋‘๋ฉด ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  start, join, await๋ฅผ ํ˜ธ์ถœํ•ด active์ƒํƒœ๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‹ค๋งŒ ์กฐ์‹ฌํ•ด์•ผ๋  ๊ฒƒ์€ await๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•˜๊ณ  ์ข…๋ฃŒ๋˜๋Š” ๊ฒƒ๊นŒ์ง€ ํฌํ•จํ•˜๋Š” ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋ฆฌ active๋กœ ๋ฐ”๊พธ์ง€์•Š์œผ๋ฉด ์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ๋Š” ์ˆœ์ฐจ์‹คํ–‰๊ณผ ์™„์ „ํžˆ ๋™์ผํ•œ ์ƒํ™ฉ์ด ๋œ๋‹ค. 

Global scope

๊ธ€๋กœ๋ฒŒ ์Šค์ฝ”ํ”„๋ฅผ ์ด์šฉํ•˜๋ฉด suspend ๋ฅผ ์•ˆ๋ถ™์ด๊ณ ๋„ suspend fun์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. @OptIn ์–ด๋…ธํ…Œ์ด์…˜์ด ํ•„์š”ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋ฃจํ‹ด ๋ฐ–์—์„œ ์ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

// The result type of somethingUsefulOneAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}

์‹คํ–‰์‹œ๊ฐ„์œผ๋กœ ๋ณด๋ฉด ๊ทธ๋ƒฅ ์ฝ”๋ฃจํ‹ด์Šค์ฝ”ํ”„ ๋‚ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค 100ms ๋” ๋Š๋ฆฌ๋‹ค. 

 

๊ธ€๋กœ๋ฒŒ ์Šค์ฝ”ํ”„๋Š” ์•ฑ ์ƒ๋ช…์ฃผ๊ธฐ ์ „์ฒด์— ์กด์žฌํ•˜๊ณ  ์ทจ์†Œ์‹œํ‚ฌ ์ˆ˜ ์—†๋‹ค. ์ด๋Ÿฐ ํ˜•ํƒœ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ ๊ฐ™์€ ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๋ฅผ ๋‚ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š”๋ฐ ์ด๊ฑด structured concurrency๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

structured concurrency

์ฝ”๋ฃจํ‹ด์˜ ์ œ์ผ ํฐ ๊ฐœ๋…์ธ structured concurrency์ด๋‹ค. ์ž์‹ ์ฝ”๋ฃจํ‹ด์ด ๋ถ€๋ชจ ์ฝ”๋ฃจํ‹ด์•ˆ์—์„œ ์กด์žฌํ•˜๊ณ , ์ž์‹์ฝ”๋ฃจํ‹ด์—์„œ ์ƒ๊ฒจ๋‚œ ์˜ˆ์™ธ๊ฐ€ ๋ถ€๋ชจ๊นŒ์ง€ ์ „ํŒŒ๋˜๋Š” ํ˜•์ƒ์„ ์ด์•ผ๊ธฐํ•˜๋ฉฐ ์Šค์ฝ”ํ”„ ์•ˆ์—์„œ ๋‹ค๋ฃจ๊ธฐ๋•Œ๋ฌธ์— ๋ณ€์ˆ˜๋ฅผ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ง€๊ธˆ๊นŒ์ง€ ์ ์—ˆ๋˜ ์ฝ”๋“œ๋ฅผ ํ•จ์ˆ˜ํ™” ์‹œ์ผœ์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ธ๋ฐ one.await()+two.await()๋ถ€๋ถ„๊นŒ์ง€ ํ•จ์ˆ˜๋กœ ๋ฝ‘๋Š” ๊ฒƒ์ด๋‹ค.

suspend fun concurrentSum(): Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    one.await() + two.await()
}
val time = measureTimeMillis {
        println("The answer is ${concurrentSum()}")
    }

suspend fun ํ•˜๋‚˜๋ฅผ ๋”ฑ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์œ„ ๊ณผ์ •์„ ๋‹ค ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ exception๋„ ๋‹ค ์ „ํŒŒ๋œ๋‹ค.

"๋Œ“๊ธ€, ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์‹œ๋ฉด ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค"
๋ฐ˜์‘ํ˜•

'Android ๐Ÿ–ฅ๏ธ > Coroutine๐Ÿ“–' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Coroutine exceptions handling  (0) 2024.03.29
Asynchronous Flow - Intermediate flow operators~  (1) 2024.03.29
Coroutine context and dispatchers  (0) 2024.03.29
Coroutine Cancellation and timeouts  (0) 2024.03.29
Coroutines basics - runBlocking, launch, job, coroutineScope  (0) 2024.03.29
COMMENT