Kt.Academy Kotlin Coroutines Deep Dive Summary 4부 — 코루틴 디스패처 및 스코프 함수

GodDB
6 min readMar 25, 2022

--

본 게시글은 Kt.Academy의 Kotlin Coroutines DEEP DIVE의 요약본입니다.

목차

  1. 코루틴 원리 및 빌더
  2. 코루틴 컨텍스트 및 취소
  3. 코루틴 예외 처리 및 스코프 함수
  4. 코루틴 디스패처 및 스코프 함수 — 현재 게시글
  5. 코루틴 테스트
  6. Flow 빌더 및 기본 Operator
  7. Flow Operator

코루틴 디스패처

코루틴에서 중요한 부분 중 하나는 코루틴이 실행 될 스레드 혹은 스레드 풀을 결정하는 것입니다.

그래서 코루틴 디스패처를 통해 코루틴이 실행될 스레드를 지정할 수 있습니다.

Default

디스패처를 별도로 지정하지 않으면 Default로 지정됩니다. Default는 CPU 집약적인 작업을 수행할 때 사용하며, 스레드 풀이 생성되는데 이 스레드 풀에 생성되는 스레드의 수는, 실행되는 컴퓨터의 코어 갯수와 동일하게 생성됩니다.

Dispatcher 제한

동시에 구동 될 수 있는 Dispatcher의 스레드의 갯수를 제한할 수 있습니다.

private val dispatcher = Dispatchers.Default.limitedParallelism(5)

보통 Default Dispatcher에 제한을 걸거나 하지는 않지만 IO Dispatcher를 제한하는 데에는 사용합니다.

limitedParallelism()은 Kotlin Coroutine 1.6.0이상 버전에서 지원되는 기능입니다.

Main

Android 프레임워크에는 가장 중요한 스레드인 UI 스레드의 개념이 있습니다. Android에서 UI와 상호 작용하는 데 사용할 수 있는 유일한 스레드입니다. 따라서 매우 자주 사용하게 되고, 세심한 주의가 필요합니다.

Main.immediate

코루틴을 main 스레드로 suspending 없이 즉시 실행 시킬 수 있는 디스패처 입니다.

IO

파일 읽기/쓰기, Android Shared Preference 등에 접근 할 때 사용하는 Dispatcher입니다. IO 스레드도 스레드풀에 관리되며, 기본적으로 스레드 64개로 생성됩니다(코어 수가 더 많을 경우엔 코어 수)

기본적으로 너무나 많은 스레드가 동작 중일때는 성능이 낮아 질 수 있습니다. 또한 너무나 많은 스레드가 존재할 경우엔 OOM이 발생할 수도 있습니다.

그렇기 때문에 필요하다면 limitedParallelism(count) 을 통해서 스레드 갯수를 제한할 수 있습니다.

또한 이와 반대로 아주 많은 스레드가 필요할 경우엔 갯수를 늘려 한번에 집중된 리소스를 사용할 수 있도록 할 수 있습니다.

IO와 Default 스레드 풀의 관계

Default와 IO스레드는 공유됩니다. 그렇기 때문에 사실상 동일한 스레드가 IO(64개), Default(코어 갯수) 만큼 생성됩니다.

이들은 각각의 스레드 풀에서 관리되고, 필요에 따라 공유됩니다.

공유되는 경우는 Default <-> IO로 컨텍스트 스위칭이 발생될 때 공유됩니다.

코루틴 스레드 풀 관리하기

Coroutine 버전 1.6 미만에서는 limitedParallelism(count) 이 없었습니다.
그렇기 때문에 스레드풀 에서의 스레드 갯수를 마음대로 늘리고, 줄이기 위해서는 별도의 스레드풀을 생성해서 관리 했습니다.

하지만 이제 limitedParallelism(count) 로 인해 스레드의 갯수를 마음대로 조정 할 수 있어서 별도로 스레드 풀을 만들 이유가 일반적으론 없습니다.

또한 코루틴 내부에서 close() 처리를 알아서 해줌으로, 별도로 개발자가 close()를 할 필요가 없어졌습니다.

코루틴 단일 스레드 생성

필요에 따라 단 하나의 스레드 로만 처리를 하고 싶은 경우가 있을 수 있습니다.

그럴 경우엔 기존에는 다음과 같은 방법으로 처리 했었습니다.

하지만 이제는 limitedParallelism(1) 로 지정하여 단일 스레드로 처리하게 할 수 있습니다.

그에 따라 별도의 close()처리가 필요하지 않아 편리합니다.

코루틴 스코프

현재 안드로이드 앱의 거의 모든 아키텍처는 MVC, MVP, MVVM으로 구성되어 있습니다. 그리고 MVP, MVVM으로 구성 했을 때, 프레젠테이션 로직은 Presenter, ViewModel이 처리합니다. 일반적으로 이 계층에서 코루틴이 시작됩니다.

그 외 Usecase, Repository 등에서는 suspend 함수만 정의됩니다.

만약 우리가 ViewModel을 만든다면 아래와 같이 만들 수 있을 것입니다.

ViewModel의 end point는 거의 대부분 ui를 향하므로, Dispatcher.Main로 지정합니다.

이제 여기에 코루틴을 취소 가능하게 만들어야 합니다.

하지만 이렇게 코루틴 전체를 취소 시켜 버린다면, 이 scope는 재활용이 불가능합니다. 그렇기에 자식 코루틴만 취소될 수 있게 만듭니다.

그리고 우리는 Exception로 부터 독립적으로 만들고 싶습니다. 이 scope를 사용하는 다른 코루틴이 Exception을 발생 시켰다고 해서, 다른 코루틴이 영향을 받아선 안됩니다.

예제 기준으론 User 정보를 가져올 때 Exception이 발생 했다고 해서,
News 정보를 가져오지 못해선 안됩니다.

그렇기 때문에 JobSupervisorJob으로 변경합니다.

만약 Exception이 발생할 때 마다, 사용자에게 다이얼로그를 띄워 주고 싶다면 아래와 같이, Exception을 전달 받을 수 있는 Handler를 연결 합니다.

현재까지 정의한 내용들은 viewModelScope와 매우 유사합니다. 그래서 사실 아주 특수한 경우를 제외하고는 ViewModel에서는 viewModelScope를 사용하는 것이 권장됩니다.

또한 액티비티, 프래그먼트에서도 코루틴을 사용해야할 경우가 존재합니다.
그 역시 새롭게 만들기 보다는, lifecycleScope을 사용하기를 권장 합니다.

lifecycleScopeviewModelScope와 유사합니다. 차이점이라곤 viewModelScope는 전달 받은 lifecycleOwneronDestroy() (Configuration change가 아닌 경우) 되었을 때 코루틴을 취소하며,

액티비티의 lifecycleScope는 액티비티가 onDestory() 될 때,
프래그먼트의lifecycleScope는 프래그먼트가 onDestory()될 때, viewLifecycleOwner.lifecycleScope는 프래그먼트가 onDestoryView()가 호출 될 때 코루틴을 취소합니다.

참고자료

--

--