[Kotlin]Coroutine 기본

한상욱·2024년 11월 10일
0

CS&자격증후기&잡담

목록 보기
22/23
post-thumbnail

들어가며

Kotlin 언어를 접하다보면 코루틴(Coroutine)을 통한 비동기와 동시성 처리에 대해서 접하게 됩니다. 그렇다보니 Kotlin에서 지원하는 특별한 솔루션이라고 생각하실지도 모르지만, 여러 프로그래밍 언어에서도 지원하는 방식입니다.

오늘은 Kotlin의 코루틴(Coroutine)에 대해서 공식문서를 파헤쳐보며 알아보도록 하겠습니다.

코루틴(Coroutine)이란?

이 단어는 협력을 의미하는 Co와 루틴을 의미하는 Routine이 합쳐진 단어로, 함께 동작하면서 규칙있는 일의 순서를 의미합니다. 이를 잘 이해하기 위해서는 루틴에 대해서 조금 알아봐야겠습니다.

프로그래밍에 어느정도 익숙한 분들께서는 대부분의 언어는 Main Thread에서 실행되고 이 안에 여러 서브 루틴이 되는 함수들이 존재하는 것을 이미 알고있습니다.

위 그림은 메인 루틴과 서브 루틴의 관계를 대략적으로 도시화한 것입니다. 이렇게 Main 함수 하위에 여러 함수들이 존재하기에 일반적인 동기적인 방식은 Main Thread가 코드를 따라 쭈욱 루틴들을 순회하는 방식으로 진행됩니다.

이러한 방식에는 하나의 문제가 발생할 수 있습니다. 너무 자원이 많이 소요되는 작업에 경우 하나의 스레드로 처리하기에는 오랜 시간이 걸릴 수 있다는 것이죠. 이러한 방식을 해결하기 위해서 동시성, 비동기 프로그래밍, 멀티 스레드와 같은 솔루션이 존재하는데, Kotlin은 Coroutine을 통해서 비동기 처리를 할 수 있습니다.

코틀린 공식문서에서 코루틴은 다음과 같이 설명합니다. 코루틴은 일시중단이 가능한 계산의 인스턴스이다. 개념적으로는 스레드이지만 나머지 코드와 동시에 실행이 가능한 코드 블록을 실행하는 것이다.

음, 아직은 애매모호한 부분이 있는 것 같지만 이는 예를 통해서 점진적으로 알아가봅시다.

코루틴과 루틴?

일반적으로 루틴들은 실행이되면 메모리에 올라가 반환 키워드를 통해 종료하여 다시 원래의 호출위치로 돌아오게 됩니다. 하지만 코루틴들은 루틴이 종료되기 전에 일시 정지하여 다른 루틴을 실행하고 다시 원래의 루틴으로 돌아와 남은 동작을 수행하는 것이 가능합니다.

이는 내부적으로 State Machine을 이용하여 동작합니다. 제어 정보인 Contuniation을 전달하며 Context를 유지하며 코드블록을 구분하여 각 코루틴들을 실행하는 것이죠.

첫번째 코루틴

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}

>> Hello
>> World!

이 코드는 공식문서에서 제공하는 첫번째 코루틴 예제입니다. runBlocking이라는 코루틴 빌더를 통하여 코드를 실행하며 위와 같은 실행결과를 보여줍니다. 이는 최상단에서 코루틴이 실행되는 동안 스레드를 차단하는 역할을 하며, 예제에서만 자주 보입니다. 이는 구조화된 동시성 원칙에 따라서 최상단에 선언된 것입니다.

launch 역시도 코루틴 빌더입니다. 이 안에는 delay를 통해 지연을 주며, "World!"라는 문자열을 출력하도록 합니다.

luanch 밖에는 "Hello"라는 문자열을 출력하는 함수가 작성되어 있습니다. 실질적인 코루틴 생성은 launch를 통해 이루어지며, Hello와 lauch 코루틴 빌더가 동시에 실행되었습니다. 즉, 하나의 스레드 내부에서 동시에 코드가 실행된 것이죠.

구조화된 동시성 원칙

모든 새로운 코루틴은 코루틴의 생명을 한정하는 특정 코루틴 내부에서만 생성될 수 있습니다. 위에 예시에서는 luanch를 선언하기 위하여 runBlocking 코루틴 빌더를 선언한 것이 구조화된 동시성 원칙을 따른 것입니다.

suspend 함수

Suspend함수는 일시정지 가능한 함수를 선언하는 키워드로 이를 통해서 코루틴을 생성할수도 있습니다.

fun main() = runBlocking {
    doWorld()
}

suspend fun doWorld() = coroutineScope {  // this: CoroutineScope
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")
}

위 코드는 첫번째 코루틴과 동일하게 동작하지만, 코루틴을 suspend함수로 리팩토링하여 동일한 동작을 수행할 수 있도록 분리한 것입니다. 이렇게 suspend 키워드를 통하여 일시정지 가능한 함수의 선언을 하고 코루틴을 정의할 수 있습니다.

명확한 작업

luanch 코루틴 빌더는 작업을 완료하는 경우 Job 오브젝트를 반환합니다. 이를 이용하여 코루틴이 명확하게 종료될 때까지 기다리는 용도로 이용할 수 있죠.

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") 

참고

코루틴(Coroutine)에 대하여
코틀린 코루틴 베이직

profile
자기주도적, 지속 성장하는 모바일앱 개발자가 되기 위해

0개의 댓글