fun postLogin(data: RequestLoginDto) {
viewModelScope.launch(Dispatchers.IO) {
runCatching {
loginService.login(data)
}
...
}
}
솝트 과제를 하면서 Dispatcher에 대해 알게 되었다. Dispatcher이 무엇일까?
dispatch는 보내는 의미를 가지고 있다.
모든 작업은 Thread 위에서 실행된다. 이때 Dispatcher는 Coroutine을 어느 Thread에 보낼지 정하는 객체이다. Coroutine을 만들어 CoroutineDispatcher로 실행을 요청하면, CoroutineDispatcher는 사용할 수 있는 Thread Pool의 Thread 중 하나에 Coroutine을 보낸다.
launch(Dispatchers.Main) {
// Coroutine이 실행할 작업
}
fun main() {
runBlocking {
launch {
println("main: 쓰레드는 ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) {
println("Unconfined: 쓰레드는 ${Thread.currentThread().name}")
}
launch(Dispatchers.IO) {
println("IO: 쓰레드는 ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) {
println("Default: 쓰레드는 ${Thread.currentThread().name}")
}
}
}
위 코드를 실행하면 다음과 같은 결과가 나온다
정리하자면, Default와 IO는 Worker Thread에서 실행되었고, Main과 Unconfined는 Main Thread에서 실행되었다.
두 Dispatcher의 차이를 알아보자
위에서 Default는 CPU 개수만큼 Thread를 생성해 작업한다고 했다. 즉, CPU 코어 개수보다 더 많은 Thread를 사용하게 되면 오버헤드가 발생하게 된다.
오버헤드?
작업1과 작업2를 번갈아가며 수행한다고 하자. 작업1을 하다가 기록을 하고, 작업 2를 하다가 다시 작업 1을 할 때 다시 불러오는 작업이 필요하다. 이때 작업 전환을 문맥 교환(Context Switching)이라고 하고 이때 낭비되는 시간이 오버헤드이다.
그래서 Default는 최대 개수를 CPU 개수만큼 제한을 둔다. 즉, 한 명에게 하나의 작업만 시킨다.
IO는 Thread가 Blocking 되기 때문에 더 많은 Thread를 사용하기 위해 대기시간이 있는 작업들을 수행한다.
Unconfined는 해당 코루틴을 호출한 Thread에서 실행하게 하는데, 사실은 이 Thread에서 실행을 시작해도 코루틴의 첫 번째 중단점까지만 실행된다. 다시 재개될 때에는 Suspend Function이 호출된 Thread에서 재개된다. Unconfined Dispatcher는 CPU time을 소비하지 않고 UI와 같은 공유 데이터를 update하지 않는 등의 경우에 사용하면 적절하다.
launch(Dispatchers.Unconfined) {
println("Unconfined : 나는 ${Thread.currentThread().name} 에서 돈다")
delay(500)
println("Unconfined : 딜레이 이후에는 ${Thread.currentThread().name} 에서 돈다")
}
launch {
println("main runBlocking : 나는 ${Thread.currentThread().name} 에서 돈다")
delay(1000)
println("main runBlocking : 딜레이 이후에는 ${Thread.currentThread().name} 에서 돈다")
}
runBlocking으로부터 상속받은 context로 동작하는 코루틴은 main Thread에서 재개되었고, Unconfined는 DefaultExecutor Thread에서 재개되었음을 확인할 수 있다. 즉 Unconfined Dispatcher는 해당 코루틴을 호출한 Thread에서 실행을 하게하고 중단 이후에는 그저 불러주는 Thread에서 실행된다는 것이다. 여기에서는 DefaultExecutor에서 delay()가 호출된 것이다.
공식 문서에 의하면 Unconfined Dispatcher는 코루틴의 일부 작업이 즉시 수행되어야해서 Thread 전환을 하고자 코루틴이 잠시 dispatch되어 동작이 나중에 수행되는 등 side effect가 발생할 수 있어 위험하다고 한다. 따라서 일반적인 경우에는 Unconfined Dispatcher를 사용하지 않는 것이 좋다
[참고자료]
https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#unconfined-vs-confined-dispatcher