DEV Community

Juyae
Juyae

Posted on

Coroutine : Light Weight Threads for Parallel Programing

Coroutine

Coroutine은 Thread/ AsyncTask/Rx 작업을 대신할 수 있는 Asynchronous/Non-Blocking Programming을 제공하는 Light-Weight Threads 로 가볍고 유연한 병렬 프로그래밍을 위한 기술

Kotlin 코루틴으로 앱 성능 향상

코루틴을 왜 사용할까 ? 크게 5가지로 알아보자

1.비동기 코드 작성

  • 네트워크 호출이나 디스크 작업과 같은 장기 실행 작업을 관리하면서 앱의 응답성을 유지하는 깔끔하고 간소화된 비동기 코드 작성 가능

2.장기 실행 작업 관리

  • 장기 실행 작업을 처리하는 두 작업을 추가하여 일반 함수를 기반으로 빌드
  • invoke 및 return 외에도 suspend 및 resume 추가

3.스택 프레임

  • 로컬 변수와 함께 실행 중인 함수 관리
  • Coroutine 정지하면 현재 스택 프레임이 복사되고 저장되어 재개될 시 저장된 위치에서 다시 복사되고 실행
  • 일반적인 순차 차단 요청처럼 보일 수도 있지만 네트워크 요청이 기본 스레드를 차단하지 않도록 함

4.기본 안전

  • Kotlin에서 모든 코루틴은 기본 스레드에서 실행 중인 경우에도 디스패처에서 실행
  • Kotlin에서 자체적 정지될 수 있으며 디스패처는 Coroutine 재개를 담당

    • Dispatchers.Main - 이 디스패처를 사용하여 기본 Android 스레드에서 코루틴을 실행합니다. 이 디스패처는 UI와 상호작용하고 빠른 작업을 실행하기 위해서만 사용해야 합니다. 예를 들어 suspend 함수를 호출하고 Android UI 프레임워크 작업을 실행하며 LiveData 객체를 업데이트함
    • Dispatchers.IO - 이 디스패처는 기본 스레드 외부에서 디스크 또는 네트워크 I/O를 실행하도록 최적화되어 있음 예를 들어 회의실 구성요소를 사용하고 파일에서 읽거나 파일에 쓰며 네트워크 작업을 실행
    • Dispatchers.Default - 이 디스패처는 CPU를 많이 사용하는 작업을 기본 스레드 외부에서 실행하도록 최적화되어 있습니다. 예를 들어 목록을 정렬하고 JSON을 파싱

5.Background Task가 필요한 대표적인 경우

  • 네트워크 Request (Retrofit, OkHttp3, UrlConnection 등)
  • 내부 DataBase (Room, SQLite 등)

Thread와 차이점

Thread

  • OS의 Native Thread에 직접 링크되어 동작하므로 많은 시스템 자원을 사용

  • Thread간 전환 시에도 CPU의 상태 체크가 필요하므로 그만큼의 비용이 발생

Coroutine

  • Coroutine은 즉시 실행하는게 아니며, Thread와 다르게 OS의 영향을 받지않기 때문에 그만큼의 비용 절약

  • Coroutine 전환 시 Context Switch가 발생하지 않음

  • 개발자가 직접 루틴을 언제 실행할지, 언제 종료할지 모두 지정이 가능

  • 이렇게 생성한 루틴은 작업 전환 시에 시스템의 영향을 받지 않아, 그에 따른 비용이 발생하지 않음

CoroutineScope& GlobalScope

  • CoroutineScope는 코루틴의 범위, 블록을 묶음으로 제어할 수 있는 단위
  • GlobalScope는 CoroutineScope의 한 종류로 Top-level Coroutine, Application의 생명주기에 종속적으로 존재

CoroutineContext

  • CoroutineContext는 코루틴을 어떻게 처리할 것인지에 대한 정보 집합 , 주요 요소로는 Job & Dispatcher 존재

Dispatcher

  • Dispatcher는 CoroutineContext의 주요 요소
  • 위에서 말했던 CoroutineContext를 상속 받아 어떤 스레드를 어떻게 동작할 것인가에 대한 정의

Coroutine 권장사항

디스패처 삽입
새 코루틴을 만들거나 withContext 를 호출할 때 Dispatchers 를 하드코딩 하지 말 것

// DO inject Dispatchersclass NewsRepository(    private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default) {    suspend fun loadNews() = withContext(defaultDispatcher) { /* ... */ }}// DO NOT hardcode Dispatchersclass NewsRepository {    // DO NOT use Dispatchers.Default directly, inject it instead    suspend fun loadNews() = withContext(Dispatchers.Default) { /* ... */ }}
Enter fullscreen mode Exit fullscreen mode

정지 함수는 기본 스레드에서 호출하기에 안전해야 함
정지 함수는 기본 스레드에서 호출하기에 안전한 기본 안전 함수여야 함

다음과 같은 패턴을 통해 앱의 확장 가능성이 높아지는데
이는 정지 함수를 호출하는 클래스가 작업 유형에 어떤 Dispatcher 를 사용할 지 걱정할 필요가 없음 . 작업을 실행하는 클래스에 책임이 있음

ViewModel은 코루틴을 만들어야 함

  • 뷰는 코루틴을 직접 트리거하여 비즈니스 로직을 실행하면 안 되고 이 책임을 ViewModel에 맡겨 비즈니스 로직을 더 쉽게 테스트 가능
  • 또한 작업이 viewModelScope에서 시작되면 코루틴이 구성 변경에도 자동 유지

예외에 주의

  • 코루틴에서 발생하는 예외를 잘못 처리하면 앱이 비정상 종료 될 수 있음 따라서 다음과 같은 코드로 예외를 포착

Conclusion

위의 내용을 정리하면서 코루틴에 대한 전반적인 이론을 알아봤다.
다음 글에서는 코루틴을 적용한 예시 코드로 실습하려고 한다.

Top comments (0)