-
withContext 와 async 를 언제 어떻게 쓰면 좋을까?
-
동일한 구문을 돌리는 경우 withContext 가 async 보다 약 2배의 성능이 좋았다는 테스트 결과가 있다.
https://stackoverflow.com/questions/50230466/kotlin-withcontext-vs-async-await
for(1..1_0000_0000){
withContext(context){
// do sth
}
}
vs
for(1..1_0000_0000){
async(context){
// do sth
}.await()
}
메모리 사용량은 큰 차이가 없다고 한다.
-
직접 성능 측정해 보았다.
launch {
val duration = measureTimeMillis {
for (i in 1..10_0000) {
withContext(Dispatchers.Default) {
Log.e("cklee", "throw exception")
}
}
}
Log.e("cklee", "duration = $duration ms")
}
// duration = 11653 ms
launch {
val duration = measureTimeMillis {
for (i in 1..10_0000) {
async(Dispatchers.Default) {
Log.e("cklee", "throw exception")
}.await()
}
}
Log.e("cklee", "duration = $duration ms")
}
// duration = 12599 ms
10만번에 대한 로그를 찍는 행위는 12599 - 11653 = 946ms 약 1초 차이가 났다.
10회만 테스트 했을 때 둘 다 6~7ms 가 걸렸다.
간단한 1~2회성 작업에 대해서는 사실 큰 차이가 나지 않는다고 볼 수 있고, 엄청난 양의 작업을 할 때는 parallel 이 필요 없다면 withContext 를 쓰는 정도로 이해해야 맞겠다.
-
async 는 parallel job 을 수행할 수 있지만, withContext 는 sequential 만 가능하다.
-
main thread 의 async 의 경우 exception 이 발생시 catch 를 해도 coroutine scope 까지 전달이 되어 앱이 죽는다.
launch {
try {
async(Dispatchers.Default) {
Log.e("cklee", "throw exception")
throw IllegalStateException("This is exception")
}.await()
} catch (e: Exception) {
Log.e("cklee", "exception $e")
}
}
// throw exception
// exception java.lang.IllegalStateException: This is exception
// <Crash>
val defaultExceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> Log.e("cklee", "coroutine exception handler $throwable") }
launch(defaultExceptionHandler) {
try {
async(Dispatchers.Default) {
Log.e("cklee", "throw exception")
throw IllegalStateException("This is exception")
}.await()
} catch (e: Exception) {
Log.e("cklee", "exception $e")
}
}
// throw exception
// exception java.lang.IllegalStateException: This is exception
// coroutine exception handler java.lang.IllegalStateException: This is exception
defaultExceptionHandler 를 쓰지 않고 앱이 crash 나지 않게 하는 방법 중 하나는 supervisorScope{ } 의 사용이다.
-
그러나 withContext 의 경우 해당 구문을 try - catch 로 쉽게 잡을 수 있다.
launch {
try {
withContext(Dispatchers.Default) {
Log.e("cklee", "throw exception")
throw IllegalStateException("This is exception")
}
} catch (e: Exception) {
Log.e("cklee", "exception $e")
}
}
// throw exception
// exception java.lang.IllegalStateException: This is exception
val defaultExceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> Log.e("cklee", "coroutine exception handler $throwable") }
launch(defaultExceptionHandler) {
try {
withContext(Dispatchers.Default) {
Log.e("cklee", "throw exception")
throw IllegalStateException("This is exception")
}
} catch (e: Exception) {
Log.e("cklee", "exception $e")
}
}
// throw exception
// exception java.lang.IllegalStateException: This is exception
withContext 의 경우 coroutineScope 으로 exception 이 전파되지 않기 때문에 defaultExceptionHandler 에 걸리지 않는다.
끝
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
[Effective Kotlin] Item1 : Limit mutability (0) | 2022.02.15 |
---|---|
[coroutine] Back-pressure 대응하기 (0) | 2022.02.02 |
[Coroutine] Exception handling in coroutine (0) | 2022.01.30 |
[Coroutine] Coroutine scope functions (0) | 2022.01.29 |
[coroutine] Flow vs RxJava (0) | 2021.05.10 |
댓글