✍🏻study/👊 kotlin

코루틴이란?

peacekim 2024. 7. 7. 21:21
반응형

그동안 멈춰왔던, 글쓰기를 시작해보려고 합니다.

현재 내가 쓰는 언어인 kotlin에 대해서 좀 더 알아보고, 공부할 수 있는 시간을 가져보겠습니다.

kotlin docs에 있는 코루틴에 관한 내용 번역과 제가 공부한 내용들을 정리해보겠습니다.

 

코루틴이란?

코루틴은 suspendable computation이다. 개념적으로 쓰레드와 비슷하다. 나머지 코드를 실행시키는 동시에, 코루틴에서 로직을 실행시킨다는 점에서 비슷히다. 하지만, 코루틴은 특정 쓰레드에 국한되지 않는다. 한 쓰레드에서 실행을 멈추고 다른 쓰레드에서 실행을 시킬 수 있다.

코루틴을 경량의 쓰레드로 생각할 수 있다. 하지만, 실제 사용에서 쓰레드와 매우 다르게 만드는 중요한 차이점이 몇 가지 있다.

여기서 suspendable computation이란 일시 중단(suspend)될 수 있는 계산 또는 연산을 의미한다. 코루틴 내에서 실행되는 코드는 일시적으로 중단되었다가 나중에 재개될 수 있다. 이는 특히 비동기 프로그래밍에서 유용하다.

suspend 함수는 호출 시 현재 진행 중인 작업을 일시 중단하고 다른 작업을 실행할 수 있게 해줘 블로킹 없이 효율적인 동시 실행이 가능하다. 

fun main = runBlocking { // CoroutinScope
    launch { // 새로운 coroutine
    	delay(100L)
        println("World!")
    }
    println("Hello")
}

결과값:

Hello
World!

 

여기서 launch는 coroutine builder이다. 새로운 코루틴을 만들고, 동시에 launch 밖에 있는 다른 코드도 실행된다. Hello가 먼저 실행되는 이유는 delay가 suspend function으로 코루틴을 블락을 시킨다. 하지만, 메인 쓰레드를 블락시키진 않기 때문에 코드의 나머지 부분인 println("Hello") 가 실행 될 수 있는 것이다. 

runBlocking은 또한 코루틴 빌더이자, non-coroutine world와 연결해주는 다리역할을 한다. 즉, runBlockingCoroutineScope를 만들어준다. runBlocking은 이름 그대로 쓰레드 working하는 것을 멈춘다는 뜻이다. runBlocking안에 있는 coroutine이 끝날때까지 쓰레드가 멈춘다. runBlocking은 메인 쓰레드를 점유하고 멈추기 때문에, 리소스가 낭비될 수 있어, application에 최상위 호출 부나 테스트코드에 쓴다.

launch는 CoroutineScope에서만 정의될 수 있기 때문에, 만약 runBlocking을 삭제한다면, 에러가 발생할 것이다.

 

Structured Concurrency

코루틴structured concurrency 법칙을 따른다. structured concurrency 법칙은 새로운 코루틴은 코루틴의 생애주기를 정하는 특정한 CoroutineScope 에서만 실행될 수 있다는 법칙이다.

structured Concurrency 법칙

  • 코루틴의 수명을 명확히 관리하고, 부모 코루틴이 자식 코루틴의 완료를 기다리도록 하는 패턴.
  • 실제 애플리케이션에서 많은 코루틴을 실행하는데, 코루틴들을 lost하거나 leak되지 않도록 보장한다.
  • 외부 스코프에서는 모든 자식 코루틴들이 완료될 때까지 완료되지 않는다.
  • Structured Concurrency 법칙은 코드에서 발생하는 모든 오류가 제대로 보고되고 없어지지 않도록 한다.

 

Extract function refactoring

fun main() = runBlocking { // this: CoroutineScope
    launch { doWorld() }
    println("Hello")
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}

suspend function은 coroutine 안에서 일반 함수처럼 사용될 수 있지만, 다른 suspend 함수를 사용하여, 코루틴의 실행을 중단할 수 있다는 추가 기능이 있다. 

 

Scope builder

다양한 빌더들이 제공하는 코루틴 스코프 외에도, coroutineScope 빌더를 사용하여 자신만의 스코프를 선언하는 것이 가능하다. 이 빌더는 코루틴 스코프를 생성하며, 모든 자식 코루틴들이 완료될 때까지 완료되지 않는다.

runBlockingcoroutineScope 는 자식 코루틴들이 모두 완료될때까지 기다리는 것 때문에, 비슷한 빌더처럼 보이지만, 둘의 차이는 현재 쓰레드를 멈추고 기다리는 지 여부이다. runBlocking은 현재 쓰레드를 멈추지만, coroutineScope는 쓰레드를 멈추지않고 다른 작업들이 쓰레드를 점유할 수 있게 release 해준다. 

runBlocking은 일반 함수이고, coroutineScope는 suspending function이다.

 

Scope builder and concurrency

coroutineScope 빌더는 여러 동시 작업을 수행하기 위해 모든 일시 중단 함수 내에서든 사용할 수 있다.

// Sequentially executes doWorld followed by "Done"
fun main() = runBlocking {
    doWorld()
    println("Done")
}

// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
    launch {
        delay(2000L)
        println("World 2")
    }
    launch {
        delay(1000L)
        println("World 1")
    }
    println("Hello")
}

실행결과

Hello
World1
World2
Done

두 개의 launch 블록이 동시에 실행되고, Hello가 가장 먼저 프린트되고, 차례로 World1와 World2가 프린트된다. doWolrd() suspend 함수가 끝난 이후에 Done이 프린트 된다.

 

An explicit job

launch는 coroutine builder로 Job object를 return한다. Job object를 사용하여, 명시적으로 해당 코루틴의 완료를 기다릴 수 있다.

val job = launch { // launch a new coroutine and keep a reference to its Job
    delay(1000L)
    println("World!")
}
println("Hello")
job.join() // wait until child coroutine completes
println("Done")

실행결과

Hello
World!
Done

즉, launch는 실행 시점을 정할 수 있다. 만약 job을 만든 이후에 바로 실행했다면, Wolrd! Hello Done 순서로 출력이 됐을 것이다.

Coroutines are light-weight

코루틴은 JVM 스레드보다 자원을 덜 소모합니다. 스레드를 사용할 때 JVM의 사용 가능한 메모리를 소모하는 코드는 코루틴을 사용하여 자원 한계에 도달하지 않고 표현할 수 있습니다.

 

정리

코루틴은 일시 중단(suspend)될 수 있는 계산 또는 연산인 suspendable computation이다.

코루틴은 코루틴 스코프 내에서만 코루틴 빌더를 통해서 생성할 수 있다.

코루틴 스코프 빌더

  • runBlocking
  • coroutineScope

둘의 차이는 runBlocking은 메인 쓰레드를 블록한다. corountineScope는 코루틴을 멈추고, 자식 코루틴의 작업이 끝날 때까지 쓰레드를 release한다.

코루틴 빌더

  • launch
  • async (이후에 나옴)

launch는 job을 return한다. 원하는 곳에서 join을 해서, job을 실행시킬 수 있다.

suspend function

코루틴을 일시 중단시키고 쓰레드를 양보한다. 일시 중단된 동안, 다른 코루틴이나 작업이 실행될 수 있게 한다.

suspend function은 일시 중단 함수 내부와 코루틴 내부에서만 호출이 가능하다.

 

 

반응형