Kotlin 공식문서
Sequential by default
- 기본적으로 suspend function은 sequential하게 작동한다.
suspend fun doSomethingUsefulOne(): Int {
delay(1000L)
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L)
return 29
}
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
Concurrent using async
- 위 예제에서
doSomethingUsefulOne()
와 doSomethingUsefulTwo()
가 dependency가 없다면 동시에 실행되도 될 것이다.
- 이때
Deferred<T>
를 반환하는 async
coroutine builder와 await()
함수를 쓰면 된다.
- 이는
Job
을 반환하는 launch
와 join()
의 관계와 같다.
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하게 선언만 했다가 나중에 실행시킬 수도 있다.
async
의 start
parameter에 CoroutineStart.LAZY
를 줘서 설정할 수 있다.
start
에 줄 수 있는 parameter는 CoroutineStart 문서를 보자.
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()}")
}
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)
42
} finally {
println("First child was cancelled")
}
}
val two = async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
one.await() + two.await()
}