- Today
- Total
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- n3문법
- jlpt
- errorhandling
- androidstudio
- Android
- 진짜일본어
- 코틀린
- github
- Kotlin
- CustomTab
- rxjava
- GIT
- 진짜학습지후기
- webflux
- coroutine
- pullrequest
- 책추천
- 인공지능
- ai
- suspend
- KotlinInAction
- blog
- 진짜학습지
- posting
- 일본어문법
- 안드로이드
- PR
- 일본어기초
- 책리뷰
- 학습지
코딩하는 개굴이
[Kotlin] Coroutine Dispatchers.Main 의 동작 순서 보장 (feat. Dispatchers.Main.immediate) 본문
[Kotlin] Coroutine Dispatchers.Main 의 동작 순서 보장 (feat. Dispatchers.Main.immediate)
개굴이모자 2023. 6. 18. 22:44Dispatcher 란?
코루틴의 어떤 스레드에서 돌아갈지 정의하는 역할로써, Event Loop 를 통해 작동된다. Event Loop는 Call Stack 과 Callback Queue 의 상태를 반복적으로 체크하고, 함수를 실행할 시간이 되면 큐의 첫번째 아이템을 Call Stack 에 넣고 실행한다. 이렇게 반복하는데 이것을 tick 이라고 한다.
Call Stack 이 구성되는 Dispatcher 는 아래 3가지가 있다. 각각 해당하는 스레드를 기준으로 콜스택을 구성하게 된다.
- Dispatchers.Main : UI를 구성하는 작업이 모여있는 쓰레드 풀
- Dispatchers.IO : (파일 혹은 소켓을) 읽고 쓰는 작업이 모여있는 쓰레드 풀
- Dispatchers.Default : 기본 쓰레드 풀, CPU 사용량이 많은 작업에 적합
- (Dispatchers.Unconfined 의 경우, 콜스택을 구성하지 않는다.)
코루틴 스코프에서 함수를 실행하면 해당 함수가 Callback Queue 에 추가된다. 이후 해당 함수를 실행할 때, CoroutineDispatchers.isDispatchNeeded 를 통해 Dispatch 가 필요한지 확인하고 필요하다면 해당 함수가 사용된 스코프에서 사용하고 있는 Dispatcher 에 맞는 콜스택에서 함수를 실행한다. (Unconfined 의 경우 마지막으로 함수가 실행된 콜스택에서 해당 함수를 실행한다.)
자, 여기까지 왔으면 본론으로 들어가보자.
Dispatchers.Main 이 포함된 함수의 순서
아래처럼 특정 함수에 Dispatchers.Main 이 포함되어있다고 가정하자.
아래를 실행한다면 어떤 결과가 나오게 될까?
fun main() {
println("first")
CoroutineScope(Dispatchers.Main).launch {
println("second")
}
println("third")
}
//결과
// first
// third
// second
메인 스레드에 실행시켰기 때문에 first-second-third 를 예상했을 수 있다. 그렇지만, 위에서 Dispatcher 의 동작 원리를 알아보았듯, 블록에 닿으면 Callback Queue 에 등록하고 tick 이 돈 이후에 Call Stack 에서 비동기로 실행되기 때문에 first-third-second 의 결과가 나오는 것이다.
그렇지만 비동기로는 실행해야하고 순서 또한 보장이 되어야한다면 어떻게 처리해야할까?
그럴 때, Dispatchers.Main.immediate 를 사용할 수 있다.
Dispatchers.Main.immediate
아까와 같은 코드에서 Dispatchers.Main.immediate 를 사용해보도록 하자.
fun main() {
println("first")
CoroutineScope(Dispatchers.Main.immediate).launch {
println("second")
}
println("third")
}
//결과
// first
// second
// third
원하는 결과가 나왔다. 왜일까? Dispatchers.Main.immediate 를 사용한다는건 이미 해당 함수가 메인 스레드에 있다는 것을 의미하고, Main 으로 Dispatch 를 요구하지 않는다. 따라서, 해당 함수가 Callback Queue 에 등록되고 CallStack 에서 비동기로 실행되는게 아니라 즉시 동기로 실행이 되어버린다.
immediate 는 이미 함수가 특정 스레드에 있다고 가정하고 추가적인 Dispatch 를 요구하지 않기 때문에 메인스레드에서만 가능하다. 따라서, 순서 보장과 최적화에 유용하게 사용될 수 있으므로 viewModelScope 와 lifecycleScope 에 기본 값으로 사용되고 있음을 볼 수 있다.
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
get() {
while (true) {
val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
if (existing != null) {
return existing
}
val newScope = LifecycleCoroutineScopeImpl(
this,
SupervisorJob() + Dispatchers.Main.immediate
)
if (mInternalScopeRef.compareAndSet(null, newScope)) {
newScope.register()
return newScope
}
}
}
+) withContext
- CoroutineContext 를 변경할 때 사용하며, dispatcher 가 지정되면 해당 블럭을 다른 스레드로 이동하고 완료되면 원래 Dispatcher 로 돌아온다. (원래 스레드로 돌아오는 것은 아니고 thread pool 에서 알아서 관리해준다.)
- suspend fun 혹은 코루틴 블럭 안에서 사용되어야한다.
- async 와 동일한 역할을 하나, await 을 호출할 필요 없이 결과가 리턴될 때까지 기다린다.
참고 링크
- https://programmar.tistory.com/53
- Dispatchers.Main Immediate 에 대한 이해: https://jisungbin.medium.com/dispatchers-main-immediate-의-대한-이해-9f073be21e5a
- Coroutine Context 에 대하여: https://oasisfores.com/kotlin-android-coroutine/
- withContext 란: https://devroach.tistory.com/142, https://velog.io/@ditt/코루틴#4-withcontext
- suspend란: https://kotlinworld.com/144
'안드로이드 > KOTLIN' 카테고리의 다른 글
[Android] Jetpack Compose 한 입 찍먹하기(List/Navigation/Dialog) (0) | 2024.01.21 |
---|---|
코드로 Coroutine 동작 파악하기 (0) | 2023.09.17 |
[Kotlin] Kotlin <--> Java 의 예외처리 (+ 지정 키워드 escape 하여 사용하기) (0) | 2023.05.07 |
[Kotlin] Kotlin <--> Java 클래스의 Getter, Setter 호출하기 (0) | 2023.05.07 |
[Kotlin] Kotlin의 기본 한번에 요약하기 (0) | 2023.05.02 |