중단 함수 구성에 대한 다양한 접근법
Sequential by default
순차적으로 실행
두개의 중단 함수가 다른 곳에 정의되어있다고 가정하자.
코드
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()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
실행 결과
The answer is 42
Completed in 2015 ms
일반 코틀린 코드와 마찬가지로 순서대로 호출하면 된다.
그렇다면 동시에 실행하도록 하려면 어떻게 하면 될까?
Concurrent using async
두개의 중단 함수 사이에 의존성 없이 우리가 원하는 결과를 더 빨리 얻기위해서 동시에 실행해보자!
async
vs launch
공통점
- 다른 코루틴들과 동시에 별도의 코루틴을 시작한다.
차이점
- launch
- job을 리턴
- 결괏값은 리턴하지 않음
- async
- Deferred를 리턴
Deferred
나중에 결과를 제공하겠다는 약속을 나타냄. 비동기 미래를 의미
.awit()
를 이용해서 최종 결과를 얻을 수 있다.
Deferred
도 Job
에 해당하므로 취소할 수 있다.
코드
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
실행 결과
The answer is 42
Completed in 1024 ms
두 개의 코루틴이 동시에 실행되므로 속도가 두 배 빨라진 것을 확인할 수 있다.
Lazily started async
선택적으로 async
는 start파라미터를 CoroutineStart.LAZY
로 설정함으로써 lazy
하게 만들 수 있다.
이 모드는 await
에 의해 결과가 호출되거나 Job
이 start()
로 호출될 때 코루틴이 시작된다.
코드
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")
실행 결과
The answer is 42
Completed in 1017 ms
두개의 코루틴이 정의되어 있지만, start를 호출하는 정확한 시점이 프로그래머에세 제공된다.
먼저 one이 시작되고 two가 시작된 후 개별 코루틴이 끝날 때까지 기다린다.
코드
val time = measureTimeMillis {
val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
실행 결과
The answer is 42
Completed in 2031 ms
만약 start
를 호출하지 않고, await
를 호출한다면 코루틴 실행을 시작하고 완료를 기다리기 때문에 순차적인 동작이 일어난다!
async(start = CoroutinueStart.LAZY)
는 값의 계산에 중단 함수가 포함된 경우 표준 lazy
함수를 대체하는 것이다.
Structured concurrency with async
async를 이용한 구조화된 동시성
두 개의 코루틴을 동시에 수행하고 결과의 합을 리턴하는 함수를 추출한 것이다.
async
코루틴 빌더는 CoroutineScope의 확장으로서 정의되므로 coroutineScope
함수 내에 존재해야 한다.
코드
suspend fun concurrentSum(): Int = coroutineScope {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
one.await() + two.await()
}
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
println("The answer is ${concurrentSum()}")
}
println("Completed in $time ms")
}
실행 결과
The answer is 42
Completed in 1027 ms
concurrentSum함수 내부에서 문제가 발생하면 해당 스코프에서 시작된 모든 코루틴이 취소된다.
취소는 코루틴 계층 구조에 따라 전파된다.
코드
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
try {
failedConcurrentSum()
} catch(e: ArithmeticException) {
println("Computation failed with ArithmeticException")
}
}
suspend fun failedConcurrentSum(): Int = coroutineScope {
val one = async<Int> {
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()
}
실행 결과
Second child throws an exception
First child was cancelled
Computation failed with ArithmeticException
자식 중 하나(two)가 취소되면 같은 스코프 내에 있는 one 코루틴과 부모 코루틴까지 취소된 것을 볼 수 있다.
'Android > Coroutine' 카테고리의 다른 글
[Kotlin/coroutine] 5. Asynchronous Flow (1) (0) | 2023.10.25 |
---|---|
[Kotlin/Coroutine] 4. Coroutine context and dispatchers (0) | 2023.10.24 |
[Kotlin/Coroutine] 2. Cancellation and timeouts (0) | 2023.10.22 |
[Kotlin/Coroutine] 1. Coroutines basics (0) | 2023.10.19 |