๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Kotlin

[3] Kotiln Coroutines - Suspending Function

by ๋„์บ๋ฆฌ๐Ÿฑ 2021. 7. 27.
๋ฐ˜์‘ํ˜•

Ref. ( ์•„๋ž˜ ์œ ํŠœ๋ธŒ ์˜์ƒ์€ ๊ฐ•๋ ฅ ์ถ”์ฒœ )

Youtube :  [์ƒˆ์ฐจ์›, ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋ฃจํ‹ด] https://youtube.com/playlist?list=PLbJr8hAHHCP5N6Lsot8SAnC28SoxwAU5A

Ref : https://kotlinlang.org/

 

 

 

 

[ ๊ฐ„๋‹จ ์š”์•ฝ ]

๋น„๋™๊ธฐ๋„ ์ˆœ์ฐจ์ ์œผ๋กœ

 

์ˆœ์ฐจ์ ์ด์ง€ ์•Š๊ฒŒ ๋น„๋™๊ธฐ์ ์œผ๋กœ - async

์ด์ค‘ Lazy ๋งค๊ฐœ ๋ณ€์ˆ˜์— ๋Œ€ํ•ด์„œ๋„ ์‚ดํŽด ๋ณด์•˜๋‹ค. 

 

Structured concurrency ํ˜•ํƒœ

> ์Šค์ฝ”ํ”„ ์•ˆ์—์„œ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰์‹œํ‚จ๋‹ค๋ฉด, ํ•ด๋‹น ์ฝ”๋ฃจํ‹ด๋“ค์ด ์ž˜๋ชป๋˜์—ˆ์„ ๋•Œ ์บ”์Šฌ์ด ์ „ํŒŒ ๋˜๋ฉด์„œ ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์„ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์  ์ฃผ์˜!

 


 

โ˜‘๏ธ ์ฝ”๋ฃจํ‹ด์€ ๋น„๋™๊ธฐ ์ผ์ง€๋ผ๋„ ์ˆœ์ฐจ์ ์ธ ์ฝ”๋“œ์™€ ๊ฐ™๋‹ค. (์˜์ƒ 08:20)

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

suspend fun doSomethingUsefulOne():Int{
    // ์‹ค์ œ๋กœ ์œ ์šฉํ•œ ๋™์ž‘์„ ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์„œ๋ฒ„ ํ˜ธ์ถœ ํ˜น์€ heavy ํ•œ ์—ฐ์‚ฐ์„ ํ•˜๋Š” ๋ถ€๋ถ„.
    delay(1000L)
    return 13
}

suspend fun doSomethingUsefulTwo():Int{
    delay(1000L)
    return 29
}

์ฝ”๋“œ ๊ฒฐ๊ณผ

>> 2000L ์ •๋„

 

 

 

 

 

โ˜‘๏ธ  ๋…๋ฆฝ์ ์ธ ๋‘๊ฐ€์ง€๋ฅผ ๋‘๋ฐฐ ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ช…์‹œ์ ์œผ๋กœ (async ์‚ฌ์šฉ) ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค.
- await ๋Š” async ๋๋‹๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.
- ์ด๋Ÿฐ ๊ฒƒ์„ ํ†ตํ•ด, ๋น„๋™๊ธฐ๋กœ ์ฝ”๋ฃจํ‹ด์„ ํ˜ธ์ถœํ•œ๋‹ค๋˜์ง€ ๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•œ๋‹ค๋˜์ง€ ์ž์œ ์ž์žฌ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ตํžŒ๋‹ค.

 

๋‘๊ฐœ์˜ ์—ฐ๊ด€์ด ์—†๋Š” ๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์•Œ์•„๋ณด์•˜๋‹ค.

(์š”์•ฝ : async๋ฅผ ํ†ตํ•ด์„œ ํ–ˆ๊ณ , deferred ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ณ  await๋ฅผ ํ†ตํ•ด์„œ ํ•  ์ˆ˜ ์žˆ๋‹ค.)

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

suspend fun doSomethingUsefulOne():Int{
    // ์‹ค์ œ๋กœ ์œ ์šฉํ•œ ๋™์ž‘์„ ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์„œ๋ฒ„ ํ˜ธ์ถœ ํ˜น์€ heavy ํ•œ ์—ฐ์‚ฐ์„ ํ•˜๋Š” ๋ถ€๋ถ„.
    delay(1000L)
    return 13
}

suspend fun doSomethingUsefulTwo():Int{
    delay(1000L)
    return 29
}

์ฝ”๋“œ ๊ฒฐ๊ณผ

>> 1000L ์ •๋„

 

 

 

 

โ˜‘๏ธ  async๋กœ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋ฃจํ‹ด์„ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋‚˜์ค‘์— ์‹คํ–‰ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์„๊นŒ?
async ์ž์ฒด๋Š” ์ฝ”๋ฃจํ‹ด ๋นŒ๋”์ด๋‹ค.

async()๋กœ ๋งŒ๋“ค๋•Œ ์•ˆ์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜ start์— Lazy๋ฅผ ๋„ฃ์–ด์ฃผ๊ฒŒ ๋˜๋ฉด ๋ฐ”๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.


lazy๊ฐ€ ๋„ฃ์–ด์ง€๋ฉด start ๋ฅผ ํ•ด์ฃผ๊ฑฐ๋‚˜ ํ˜น์€ await๋ฅผ ํ•ด์ค„ ๋•Œ ์ด ์ฝ”๋ฃจํ‹ด์ด ์‹คํ–‰๋˜๊ฒŒ ๋œ๋‹ค.

 

.start() ๋ฅผ ์—†์• ๋ฉด ? 2์ดˆ๊ฐ€ ๊ฑธ๋ฆผ : ์™œ์ผ๊นŒ.

์ฝ”๋ฃจํ‹ด ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ  ๋˜ํ•˜๋‚˜ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ start ํ•˜์ง€ ์•Š์•˜์œผ๋‹ˆ await ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋˜๊ณ ,

one.await์—์„œ ์‹คํ–‰๋˜๋ฉด ๋๋‚ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋‹ค์Œ ์ฝ”๋ฃจํ‹ด์ด ์‹คํ–‰ ๋˜๊ธฐ ๋•Œ๋ฌธ.

 

fun main() = runBlocking {
    val time = measureTimeMillis {
        val one = async(start = CoroutineStart.LAZY) {doSomethingUsefulOne()}
        val two = async(start = CoroutineStart.LAZY) {doSomethingUsefulTwo()}
        one.start()
        two.start()
        println("The answer is ${one.await() + two.await()}")
    }
    kotlin.io.println("Completed in ${time} ms")
}

suspend fun doSomethingUsefulOne():Int{
    // ์‹ค์ œ๋กœ ์œ ์šฉํ•œ ๋™์ž‘์„ ํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์„œ๋ฒ„ ํ˜ธ์ถœ ํ˜น์€ heavy ํ•œ ์—ฐ์‚ฐ์„ ํ•˜๋Š” ๋ถ€๋ถ„.
    delay(1000L)
    return 13
}

suspend fun doSomethingUsefulTwo():Int{
    delay(1000L)
    return 29
}

 

 

 

 

โ˜‘๏ธ  Structured Concurrency with async

Global Scope ์—์„œ Launch ํ•˜๋Š” ํ˜•ํƒœ๋กœ ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ธ์„œ ๋งŒ๋“ค์ง€ ๋ง๊ณ ,
Structured concurency ํ˜•ํƒœ๋กœ suspend ํ•จ์ˆ˜๋ฅผ ์กฐํ•ฉํ•˜๊ณ , ์ฝ”๋ฃจํ‹ด์„ ๊ตฌ์„ฑ ํ•˜๋Š”๊ฒŒ ๋‚ฎ๋‹ค.

fun main() = runBlocking {
    val time = measureTimeMillis {
        println("The answer is ${concurrentSum()}")
    }
    println("Completed in ${time} ms")
}

suspend fun concurrentSum():Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }

    delay(10)
    println("exception")

    // throw Exception()

    one.await() + two.await()
}

suspend fun doSomethingUsefulOne():Int{
    println("start, doSomethingUsefulOne")
    delay(3000L) // ์œ„์˜ exception ์œผ๋กœ cancel ์„ ์ด delay์—์„œ
    println("end,doSomethingUsefulOne ")
    return 13
}

suspend fun doSomethingUsefulTwo():Int{
    kotlin.io.println("start, doSomethingUsefulTwo")
    delay(3000L)
    kotlin.io.println("end,doSomethingUsefulTwo ")
    return 29
}

 

 

 

 

โ˜‘๏ธ  Structured Concurrency ์•ˆ์—์„œ ์‹คํ–‰๋œ async ์ฝ”๋ฃจํ‹ด 2๊ฐœ๊ฐ€ ์žˆ๋Š”๋ฐ,

์–ด๋–ค ์ฝ”๋ฃจํ‹ด์—์„œ Exception ์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, Exception์ด ๋ฐœ์ƒ๋˜์–ด์„œ ์ฝ”๋ฃจํ‹ด์ด Cancel ๋˜๋ฉด Cancel ๋œ ๊ฒƒ๋“ค์ด Hierarchy๋กœ ์ „ํŒŒ๋œ๋‹ค.

fun main() = runBlocking<Unit> {
    try{
        failedConcurrentSum()
    }catch (e: ArithmeticException){
        println("Computation failed with ArithmeticException")
    }
}

// ์ฝ”๋ฃจํ‹ด two ์—์„œ exception์ด ๋ฐœ์ƒํ•˜๋ฉด ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด one์— try~ catch~๊ฐ€๋˜์„œ finally~ ๋ธ”๋ก์ด ์ฐํžˆ๊ฒŒ ๋œ๋‹ค.
// ์ฆ‰ ์ •๋ฆฌํ•˜์ž๋ฉด, ๋‘ ๊ฐœ์˜ ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด์ด ์žˆ์—ˆ๋Š”๋ฐ ์ตœ์†Œ๊ฐ€ ์ „ํŒŒ๋œ ๊ฒƒ!
// and, ์ด๊ฒƒ์„ ๋ถˆ๋Ÿฌ์ค€ ๋ถ€๋ชจ ์ฝ”๋ฃจํ‹ด์ด ์žˆ๋Š”๋ฐ,  ์ด ์ฝ”๋ฃจํ‹ด ๋‚ด์—์„œ (์œ„์— runBlocking) ์ทจ์†Œ๊ฐ€ ์ „ํŒŒ๊ฐ€ ๋˜์–ด์„œ ๋‹ค์‹œ try~ catch~ ๋˜์–ด์„œ ์žกํžˆ๊ฒŒ ๋œ๋‹ค.

suspend fun failedConcurrentSum():Int = coroutineScope {
    val one = async<Int>{
        try{
            delay(Long.MAX_VALUE)
            42
        }finally {
            println("First child was cancelled")
        }
    }

    val two = async<Int> {
        println("Second child throws an exception")
        throw java.lang.ArithmeticException()
    }

    one.await() + two.await()
}
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€