Composing suspending functions

Kotlin 공식문서

Sequential by default

  • 기본적으로 suspend function은 sequential하게 작동한다.
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
}

val time = measureTimeMillis {
    val one = doSomethingUsefulOne() // 1 second
    val two = doSomethingUsefulTwo() // 1 second
    println("The answer is ${one + two}")
}
println("Completed in $time ms") // prints 2 seconds

Concurrent using async

  • 위 예제에서 doSomethingUsefulOne()doSomethingUsefulTwo()가 dependency가 없다면 동시에 실행되도 될 것이다.
  • 이때 Deferred<T>를 반환하는 async coroutine builder와 await() 함수를 쓰면 된다.
  • 이는Job을 반환하는 launchjoin()의 관계와 같다.
val time = measureTimeMillis {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")

Lazily started async

  • 위의 async 예제에서는 async가 있는 라인에서 바로 실행되었다.
  • 이것을 lazy하게 선언만 했다가 나중에 실행시킬 수도 있다.
  • asyncstart parameter에 CoroutineStart.LAZY를 줘서 설정할 수 있다.
  • start에 줄 수 있는 parameter는 CoroutineStart 문서를 보자.
val time = measureTimeMillis {
    val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
    val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
    // some computation
    one.start() // start the first one
    two.start() // start the second one
    println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
  • 참고로 위 코드에서 start()하지 않으면 sequential하게 동작한다. (wait()에 걸림)

Async-style functions

  • doSomethingUsefulOne()doSomethingUsefulTwo()를 아래처럼 바꿔서 asynchronous하게 할 수도 있다.
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}
  • 물론 GlobalScope를 이유없이 남발하는 것은 지양해야하므로 아래 방법을 살펴보자.

Structured concurrency with async

  • 그럼 이번에는 structured concurrency를 활용한다.
suspend fun concurrentSum(): Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    one.await() + two.await()
}

fun main() = runBlocking {
    val time = measureTimeMillis {
        println("The answer is ${concurrentSum()}")
    }
    println("Completed in $time ms")
}
  • 이렇게 하면, concurrentSum()에서 예외가 나도 scope내의 코루틴은 모두 cancel될 것이다. (GlobalScope였다면 그렇지 않을 것이다.)
  • cancel해서 exception을 내고 진행중인 coroutine이 cancel되는 아래 예제를 보자.
suspend fun failedConcurrentSum(): Int = coroutineScope {
    val one = async {
        try {
            delay(Long.MAX_VALUE) // Emulates very long computation
            42
        } finally {
            println("First child was cancelled")
        }
    }
    val two = async<Int> {
        println("Second child throws an exception")
        throw ArithmeticException()
    }
    one.await() + two.await()
}

좋은 웹페이지 즐겨찾기