Coroutine 알아보기

이완희·2024년 9월 11일

코틀린 기초 타파

목록 보기
5/5

Thread-Safe란?

멀티스레드 환경에서 여러 스레드가 자원에 접근할 때, 올바르게 동작하도록 보장한다는 것을 의미.

Mutual Exclusion, Atomicity, Visibility, Sequential Consistency를 만족해야 한다.

Blocking and Synchronous

  • Blocking vs Non-Blocking
    • Blocking: 작업을 수행할 때, 작업이 완료될 때까지 스레드가 멈추고 기다리는 방식
    • Non-Blocking: 작업을 수행할 때 스레드가 멈추지 않고 다른 작업을 처리할 수 있는 방식
  • Synchronous vs Asynchronous
    • Synchronous: 작업을 순차적으로 처리하며, 각 작업이 완료될 때까지 기다린 후 작업을 실행
    • Asynchronous: 작업이 즉시 시작되지만, 완료될 때까지 기다리지 않고 다른 작업을 동시에 처리하는 방식

코루틴은 작업을 비동기적으로 처리하며 스레드는 차단되지 않고 다른 작업을 수행하는 Non-Blocking + Asynchronous이다.

코루틴 기초

간단한 예제와 함께 코루틴을 이해해보자

fun main() = runBlocking {
    launch { doWorld() }
    println("Hello")
}

suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
  1. runBlocking{}은 코루틴 스코프를 제공한다. 해당 코드 블록이 완료될 때까지 현재 스레드를 차단한다.
  2. launch는 새로운 코루틴을 생성하고 비동기적으로 실행된다. launch { doWorld() }와 println(”Hello”)가 동시에 실행된다.
  3. suspend는 함수를 일시 중단 가능 상태로 만든다. 일시 중단된 동안에도 스레드는 차단되지 않고 다른 작업을 수행할 수 있다. 반드시 코루틴내에서만 선언되어야 한다.

Scope Builder는 runBlocking뿐만 아니라 coroutineScope도 있다.

fun main() = runBlocking {
    doWorld()
}

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

둘 다 해당 블록의 본문과 자식 코루틴들이 완료될 때까지 대기한다는 공통점이 있지만 어느 부분이 다를까?

  • runBlocking
    • 현재 스레드를 차단하므로 다른 작업을 수행하지 못하고, 코루틴이 끝날 때까지 대기한다.
    • 일반 함수이기에 어디서든 호출 가능하다.
  • coroutineScope
    • 스레드를 차단하지 않고 일시중단 한다. 메인 스레드가 다른 작업을 수행할 수 있다.
    • 코루틴 내에서만 호출해야 한다.

suspend가 일시 중단된다는 개념을 보다 잘 이해하기 위해선 코루틴과 스레드의 차이를 알아야 한다. 확실히 짚고 넘어가자.

Thread vs Coroutine

쉽게 말해 Coroutine은 Thread를 더 잘게 쪼개 사용하는 방식이다.

Thread는 개발자가 아닌 OS가 관리하며 자신만의 스택과 레지스터가 있고, 메모리 공간(Heap)을 공유한다.

Context Switching이 발생할 때, 자원을 교환하므로 비용이 상대적으로 크다.

Coroutine은 사용자 수준의 Thread로 Context Switching이 훨씬 가볍고 비용이 적게든다.

수천 개의 Coroutine 을 단일 Thread에서 실행할 수 있다. Thread를 차단하지 않고 일시 중단하여 나중에 재개한다.

출처: https://aaronryu.github.io/2019/05/27/coroutine-and-thread/

Coroutine 이 가볍다는 의미는 무엇일까?

작업의 단위는 Object이므로 Task1이 수행중에 비동기 Task2가 발생하더라도 Task1을 수행하던 같은 Thread에서 Task2를 수행할 수 있으며 Heap영역에서 발생하므로 데이터를 주고받지 않아도 된다. 단일 Thread에서 Coroutine Object만 교체하면 되므로 OS레벨의 Context Switching이 발생하지 않기에 보다 가볍다는 얘기이다.

profile
인생을 재밌게, 자유롭게

0개의 댓글