본문 바로가기
개발언어/Kotlin : 코틀린

코틀린 익히기 14 - 코루틴 Coroutine

by 개발자D 2024. 1. 14.

코틀린 익히기 14 - 코루틴 Coroutine

 

보통 한 프로세스 안에서 여러 개의 일을 동시에 진행시키기 위해서는 멀티스레드를 사용합니다. 자바 쉽게 배우기 20 - 스레드에서도 스레드에 대해 다룬 적이 있죠.

❓ 프로세스란 무엇인가요?
- 일반적으로 실행 중인 프로그램을 의미하고, 작업(Job) 혹은 태스크(Task)라고 불리기도 합니다.

❓ 멀티스레드란 무엇인가요?
- 멀티스레드는 한 프로세스 안에서 여러 개의 일이 동시에 진행되는 것을 의미합니다.

 

코틀린으로 이러한 동시성 프로그래밍을 구현할 때는 스레드뿐만 아니라 코틀린에서 기본적으로 제공하는 코루틴을 활용할 수 있습니다.

코루틴은 멀티 스레드가 가지는 단점(디버깅, 예측의 어려움, 코드 복잡도 높음)을 극복할 수 있기에 자주 사용되는 방식입니다.

 

동기와 비동기

동기 : 작업을 순서대로 진행함

비동기 : 작업을 순서와 무관하게 진행함

 

Blocking과 NonBlocking

Blocking :  호출된 함수가 작업을 완료할 때까지 호출한 쪽의 실행을 멈추고 기다립니다.

Non-Blocking : 호출한 쪽은 작업의 완료를 기다리지 않고 다른 작업을 계속 수행할 수 있습니다.

 

비동기 프로그래밍이란?

일반적으로 비동기 프로그래밍은 비동기와 Non-Blocking 방식을 동시에 사용하는 것을 의미합니다. 여러 작업을 대기 없이 순서에 무관하게 동시에 실행시키는 방식으로 프로그래밍하는 것이죠.

 

코루틴의 특징

  • 프로세스의 heap 메모리를 공유해서 사용합니다.
  • 문맥 교환이 없고 최적화된 비동기함수를 사용합니다.
  • 비선점적으로 작동합니다.
  • 협력형 멀티태스킹 구현에 적합합니다.
  • 경량: 코루틴을 실행 중인 스레드를 차단하지 않는 중단(suspend)를 지원하므로 단일 스레드에서 많은 코루틴을 실행할 수 있습니다. 중단(suspend)은 많은 동시 작업을 지원하면서도 차단보다 메모리를 절약합니다.
  • 메모리 누수 감소: 코틀린 Scope를 사용하여 범위 내에서 작업을 실행하게 합니다. 외부 범위는 모든 하위 코루틴이 완료될 때까지 완료될 수 없습니다.
  • 기본으로 제공되는 취소 지원: 실행 중인 코루틴 계층 구조를 통해 자동으로 취소가 전달됩니다.
  • Jetpack 통합: 많은 Jetpack 라이브러리에 코루틴을 완전히 지원하는 확장 프로그램이 포함되어 있습니다. 일부 라이브러리는 구조화된 동시 실행에 사용할 수 있는 자체 CoroutineScope도 제공합니다.

 

코루틴 공식 문서

 

Android의 Kotlin 코루틴  |  Android Developers

Android의 Kotlin 코루틴 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동

developer.android.com

 

 

Coroutines guide | Kotlin

 

kotlinlang.org

 

인텔리제이 코루틴 Library 추가

File > Project Structure > Libraries > + > From Maven

아래의 검색어를 입력하여 원하는 버전을 찾아 선택 후 OK를 클릭합니다.

org.jetbrains.kotlinx:kotlinx-coroutines-core:

 

코루틴 스코프 Coroutine Scope

코루틴 스코프는 코루틴의 범위를 지정하는 역할을 합니다. 코루틴 스코프에 따라 코루틴 수명이 제한되며, 코루틴 스코프 안에서 코루틴 기능을 사용할 수 있습니다.

 

또한, 코루틴 스코프는 구조화된 동시성을 제공합니다.

 

❓ 구조화된 동시성이란?

코루틴간의 부모-자식 관계와, 그 관계에 따른 종료 및 취소의 전파를 의미합니다.

  • 자식 코루틴은 부모 코루틴으로부터 코루틴 콘텍스트를 물려받습니다.
  • 부모 코루틴은 자식 코루틴이 완료될 때까지 대기합니다.
  • 부모 코루틴이 취소되면 자식 코루틴에게도 취소가 전파 됩니다.
  • 자식 코루틴에서 에러가 발생하면, 부모 코루틴에게도 에러가 전파되어 모든 코루틴이 취소됩니다.

Scope 종류

코틀린 코루틴에 정의되어 있는 Scope를 사용하거나 직접 Custom 한 Scope을 사용할 수 있습니다.

GlobalScope 애플리케이션 전체에서 코루틴을 실행할 때 사용되며, 애플리케이션이 종료될 때까지 살아 있습니다.
CoroutineScope 개발자가 직접 정의할 수 있는 스코프입니다. 이를 통해 특정 컨텍스트에서 코루틴을 실행하고 관리할 수 있습니다.
supervisorScope CoroutineScope와 유사하지만, 자식 코루틴의 예외 처리가 독립적입니다. 자식 코루틴 중 하나에서 예외가 발생해도 다른 자식 코루틴에는 영향을 미치지 않습니다.
lifecycleScope LifecycleOwner(보통 Activity나 Fragment)와 연결된 코루틴 스코프입니다.
viewModelScope ViewModel과 연결된 코루틴 스코프입니다.

 

코루틴 빌더

launch / runBlocking / async

  runBlocking  launch  async
중단시
호출 Thread
Blocking Non-Blocking Non-Blocking
반환값   Job 객체 Deferred<T> 객체
특징 Coroutine Scope 밖에서도 호출 가능

main함수, 테스트 용도로만 사용 (권장되지 않는 코루틴 빌더)
 join()을 사용하면 launch로 생성된 코루틴 작업이 끝날때까지 메인 코루틴을 기다리게 할 수 있음 결괏값을 받기위해 await() 사용

작업들간의 의존관계가 있을 때 사용함

 

import kotlinx.coroutines.*

fun main() = runBlocking { // CoroutineScope
    launch { // 새로운 코루틴을 백그라운드에서 실행함
        delay(1000L) // 1초동안 넌블로킹 지연
        println("World!")
    }
    
    val a = async {
        delay(1000L)
        return@async "A"
    }
    
    
    println("Hello") // 메인 코루틴이 이전 코루틴이 지연되는 동안 실행됨
    println(a.await())
}

// Hello
// (1초후)
// World!
// A

 

 

Job 객체란 무엇인가요?

  • Job 객체는 작업의 상태를 저장하는 객체입니다. Job객체를 통해 코루틴의 상태를 확인할 수 있습니다.

Deffered<T> 객체란 무엇인가요?

  • Job 객체를 확장한 개념으로, 비동기 작업의 결과를 반환할 수 있는 코루틴 핸들러입니다.

 

launch / async 매개변수 start

코루틴 시작 방법을 설정하는 매개변수입니다.

DEFAULT 즉시 시작 매개변수를 입력하지 않을 경우 DEFAULT로 설정
LAZY 처음에는 중단된 상태이며 start()나 await()로 시작
ATOMIC 최적화된 방법으로 시작
UNDISPATCHED 분산 처리 방법으로 시작

 

suspend fun doWork1(): String {
    delay(1000)
    return "Work1"
}

suspend fun doWork2(): String {
    delay(3000)
    return "Work2"
}

fun main() = runBlocking {
    val time = measureTimeMillis {
        val one = async { doWork1() }
        val two = async { doWork2() }
        println("${one.await() + "_" + two.await()}")
    }
    println("Completed in $time ms")
}

// Work1_Work2
// Completed in 3040 ms

fun main() = runBlocking {
    val time = measureTimeMillis {
        val one = async(start = CoroutineStart.LAZY) { doWork1() }
        val two = async(start = CoroutineStart.LAZY) { doWork2() }
        println("${one.await() + "_" + two.await()}")
    }
    println("Completed in $time ms")
}

// Work1_Work2
// Completed in 4078 ms

 

suspend 함수 (지연 함수, 중단 함수)

코루틴 기능을 지원하는 함수는 선언부에 suspend 키워드가 붙습니다.

suspend 함수는 또 다른 suspend 함수나 코루틴 블록 안에서만 사용할 수 있습니다.

현재 코루틴을 중단시키고 함수 내 작업을 수행하기 때문에 지연 함수, 중단 함수라고 부릅니다.

 

예시 ) delay()

public suspend fun delay(timeMillis: kotlin.Long): kotlin.Unit { /* compiled code */ }

코루틴에 대해 가장 기본적인 개념들만 다뤄봤습니다. 코틀린 익히기는 이것으로 마칩니다. 👏 지금까지 읽어주셔서 감사합니다. 🙇‍♀️