🚀 글 읽는 순서 🚀
코루틴은 왜 빠른 걸까요?
suspend 함수란 무엇인가요?
Coroutine Dispatcher, 넌 대체 뭐야?
-> withContext는 무엇이며 async와 무슨 차이가 있을까? (현재)
1. 요약
2. Dispatcher와 CoroutineContext
3. withContext
3-1. 개념
3-2. async와의 차이
1. 요약
🧑💻: withContext는 왜 사용할까요?? async와는 무슨 차이가 있을까요?
👨🏻🦱: 코루틴의 context를 변경하고 싶을 때나
비동기 코드를 순차적 실행할 때 사용합니다.
asnyc보다 withContext가 더 빠르나 무의미한 수준이고
async는 병렬 처리가 가능하고 withContext는 순차 처리만 가능합니다.
또, async 내에서 발생한 예외는 try~catch로 잡을 수 없으며
withContext는 예외 처리가 가능합니다.
2. Dispatcher와 CoroutineContext
// 무거운 연산에 최적화된 작업을 할때
CoroutineScope(Dispatchers.Default).launch {
}
// UI 작업을 할때
CoroutineScope(Dispatchers.Main).launch {
}
// 네트워크, DB 작업 등에 최적화된 작업을 할때
CoroutineScope(Dispatchers.IO).launch {
}
이전 게시물에서 Dispatcher에 대해 설명했지만, witchContext와 밀접한 연관이 있어서 가볍게 언급하고 넘어가려고 한다.
Dispatcher는 코루틴을 배분하는 놈이며
어떤 작업을 하고 싶느냐에 따라 그에 맞는 Dispatcher를 사용해주면 된다고 했다.
우리는 CoroutinScope를 사용할 때 CoroutineContext type을 넘겨주는데
CoroutineContext는 코루틴이 실행되는 환경 정보를 가진다.
IO 작업에 맞는 실행 환경을 가질 건지, CPU 작업에 맞는 실행 환경을 가질 건지 등의 정보가 여기에 포함된다.
이 이야기를 하는 이유는 withContext의 context가 바로 저 CoroutineContext를 의미하기 때문이다.
사전 정보는 알았으니 이제 본격적으로 withContext에 대해 알아보자.
3. withContext
3-1. 개념
withContext는 나 여기서는 이 실행 환경으로 코루틴 돌릴래요~ 라고 하는 것과 같다.
(아마 네이밍이 그래서 with context 인 듯)
위 코드를 보면 주황색으로 칠해진 부분은 Dispatcher.IO 환경에서 코루틴을 실행하고
초록색 부분에서는 Dispatcher.Main 환경에서 실행이 된다.
즉, 코루틴 실행 도중 실행 환경을 바꾸고 싶을 때 사용한다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Main).launch {
exampleSuspend()
}
}
suspend fun exampleSuspend() {
val job = CoroutineScope(Dispatchers.IO).async {
delay(1000)
}
job.await()
}
즉, async와 await을 사용하면 이렇게 작성해야 할 코드를
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Main).launch {
exampleSuspend()
}
}
suspend fun exampleSuspend() {
withContext(Dispatchers.IO) {
delay(1000)
}
}
이렇게 작성할 수 있다는 것이다.
(만약 await에 대해 모른다면 여기에서 확인할 수 있다)
suspend fun exampleSuspend() {
// 1
withContext(Dispatchers.IO) {
delay(1000) // 2
}
// 3
}
withContext 코드 내부를 까보면 결국엔 이것도 suspend 함수이기 때문에
withContext {} 내의 코드가 다 끝날 때까지 정지가 된다.
위에 주석을 달아둔 순서대로 코드가 실행이 된다는 것이다.
즉, withContext를 사용하면 순차적으로 코드가 실행되는 것처럼 구현을 할 수 있다.
3-2. async 와의 차이
[목차 3-1] 설명대로라면 withContext는 async-wait과 거의 흡사하다.
음... 코드가 조금 간결해지는구나 정도?
그러면 사용하기 쉬운 withContext를 사용하는 게 장땡일까? 그렇지 않다.
어떤 차이가 있는지 자세히 알아보자.
- withContext가 async보다 2배 성능이 좋다.
stackoverflow의 한 유저가 두 함수를 비교한 결과가 있다. (링크)
하지만 말이 2배지 사실상 속도가 nanoseconds급으로 빠르기 때문에 크게 의미가 없다고 한다.
비유를 하자면 여러분들이 하는 게임에서 뽑기 2배 이벤트를 한다고 하자.
원래 확률이 50%면 -> 100%로 늘어나니까 대박 이벤트지만
원래 확률이 0.00001%면 -> 0.00002% 니까 사실상 아무 의미가 없다.
마찬가지로 withContext와 async의 속도 차이는 사실상 아무 의미가 없다.
둘 중 뭘 사용할까에 대한 기준으로 속도는 의미가 없다는 것!
(엄청난 양의 작업을 한다면 의미가 있을지 모르지만 일반적인 경우는 아니므로..)
- 병렬 처리
suspend fun exampleSuspend() {
withContext(Dispatchers.IO) {
delay(1000)
}
withContext(Dispatchers.IO) {
delay(1000)
}
withContext(Dispatchers.IO) {
delay(1000)
}
}
withContext는 순차적인 실행은 되지만 병렬 실행이 되지 않으므로
위 코드를 실행하기 위해서는 약 3초의 시간이 소요된다.
suspend fun test() {
CoroutineScope(Dispatchers.IO).async {
delay(1000)
}
CoroutineScope(Dispatchers.IO).async {
delay(1000)
}
CoroutineScope(Dispatchers.IO).async {
delay(1000)
}
}
하지만 async를 사용하면 위 코드는 약 1초의 시간이 소요된다.
즉, 하나의 작업을 비동기 처리하는 상황에서는 withContext를 사용하는 것이 낫지만
병렬 처리를 해야 하는 상황에서는 async를 사용해야 한다는 것이다.
- 예외 처리
CoroutineScope(Dispatchers.IO).launch { // 2. 여기까지 에러가 전달된다.
try {
async {
throw Exception() // 1. 여기서 에러를 발생시키면
}
} catch (e: Exception) {
println("error catch")
}
}
async를 사용하면 exception이 발생하는 부분에서 try~catch를 사용해도
그 코루틴을 실행시킨 CoroutineScope에도 예외가 전달된다.
그래서 위 코드는 실행시키지 못하고 앱이 죽어버린다.
(정확히는 async가 그렇다기보단... launch도 마찬가지다)
CoroutineScope(Dispatchers.IO).launch {
try {
withContext(Dispatchers.Default) {
throw Exception()
}
} catch (e: Exception) {
println("error catch")
}
}
반면 withContext는 예외가 전달되지 않아 Exception을 잡아낸다는 차이가 있다.
이렇게 차이를 알아보았으니
앞으로는 상황에 맞게 withContext를 사용하자! 😄
💡 느낀 점
- withContext를 순차적 실행을 위해서만 사용했는데... 이런 특징과 차이점이 있는 줄 몰랐다. 👉👈 반성...
- async를 사용할 때 try~catch가 예외를 잡지 못한다는 것도 이번에 처음 알았다. 끙. 여태까지 예외 처리를 잘못하고 있었네...
- 왜 바깥쪽 scope에까지 예외를 전파하는 걸까? error handling에 대한 내용도 다음에 알아봐야겠다.
📘 참고한 자료
'오늘은 뭘 배울까? > Android' 카테고리의 다른 글
Bottom Sheet Dialog 예제 : Modal, Persistent, 모서리 둥글게 등 (5) | 2022.09.14 |
---|---|
안드로이드 다이얼로그 만들기(Custom Dialog까지) (7) | 2022.09.09 |
Coroutine Dispatcher, 넌 대체 뭐야? (2) | 2022.08.31 |
suspend 함수란 무엇인가요? (0) | 2022.08.29 |
코루틴은 왜 빠른 걸까요? (0) | 2022.08.27 |
댓글