티스토리 뷰

728x90

2. 코루틴 스코프 및 컨텍스트

2.1 코루틴 스코프(Coroutine Scope)란?

코루틴 스코프는 코루틴의 수명을 관리하는 역할을 합니다. 코루틴은 스코프 내에서만 실행되며, 스코프가 종료되면 그 스코프 내에서 실행된 모든 코루틴도 자동으로 취소됩니다. 이는 구조화된 동시성을 구현하는 중요한 개념으로, 비동기 작업이 여러 개 실행되더라도 일정한 흐름 안에서 안전하게 관리될 수 있도록 해줍니다.

코루틴은 여러 개의 스코프에서 동작할 수 있으며, 대표적으로 두 가지 스코프를 사용할 수 있습니다:

  • GlobalScope: 앱이 종료되기 전까지 코루틴이 계속 실행됩니다. 하지만 자원을 낭비할 수 있기 때문에 권장되지 않습니다.
  • CoroutineScope: 특정한 범위에서만 코루틴이 실행되며, 해당 범위가 끝나면 코루틴이 취소됩니다. 보통 launchasync에서 사용됩니다.

2.2 GlobalScope vs CoroutineScope

  • GlobalScope는 앱이 종료될 때까지 모든 코루틴을 지속적으로 실행할 수 있지만, 구조화된 동시성(structured concurrency)을 보장하지 않기 때문에 앱이나 프로젝트의 크기가 커질수록 비효율적이고 예외 처리도 복잡해집니다.
  • CoroutineScope는 스코프의 수명을 관리하면서 코루틴이 안전하게 종료되도록 합니다. 대부분의 경우에 CoroutineScope을 사용하는 것이 바람직하며, 특정 수명 동안만 코루틴을 실행하려면 이를 사용해야 합니다.

다음은 GlobalScopeCoroutineScope의 차이를 보여주는 간단한 예제입니다.


2.3 코루틴 스코프 사용 예제

import kotlinx.coroutines.*

fun main() {
    // GlobalScope 사용 예제
    GlobalScope.launch {
        delay(1000L)
        println("GlobalScope: I'm still running!")
    }

    // runBlocking 스코프 사용 예제
    runBlocking {
        launch {
            delay(500L)
            println("CoroutineScope: I'm running within the scope!")
        }
        println("runBlocking: Waiting for the coroutine inside to finish.")
    }

    println("Main program continues...")
    Thread.sleep(2000L)  // GlobalScope 코루틴이 실행될 시간을 주기 위해 추가한 코드
}

코드 설명

  1. GlobalScope.launch:
    • 이 코루틴은 앱의 생명주기 전체 동안 실행됩니다. GlobalScope.launch는 1초 후에 "GlobalScope: I'm still running!"을 출력하지만, 해당 코루틴은 별도의 스레드에서 실행되므로 runBlocking이 끝난 후에도 여전히 실행됩니다.
  2. runBlocking 스코프:
    • runBlocking은 현재 스레드를 블로킹하며, 그 안에서 실행된 모든 코루틴이 끝날 때까지 기다립니다. 이 스코프 내에서 코루틴이 실행되면, 스코프가 끝나는 시점에 모든 코루틴도 종료됩니다.
  3. Thread.sleep(2000L):
    • 이 코드는 GlobalScope에서 실행된 코루틴이 완료될 수 있도록 시간을 주는 용도로 삽입되었습니다. 그렇지 않으면 메인 프로그램이 먼저 종료될 수 있습니다.

출력 결과:

runBlocking: Waiting for the coroutine inside to finish.
CoroutineScope: I'm running within the scope!
Main program continues...
GlobalScope: I'm still running!
  • runBlocking 내의 코루틴은 500ms 후에 "CoroutineScope: I'm running within the scope!"를 출력한 뒤 종료됩니다.
  • 반면 GlobalScope.launch는 1초 후에 "GlobalScope: I'm still running!"을 출력합니다. 이때 메인 스레드는 이미 println("Main program continues...")를 출력하고 기다리는 상태입니다.

2.4 코루틴 컨텍스트 및 디스패처

코루틴은 **컨텍스트(Context)**에서 실행됩니다. 컨텍스트는 코루틴이 어떤 스레드에서 실행될지와 같은 실행 환경을 정의합니다. 이때 주로 사용되는 디스패처(Dispatcher)는 다음과 같습니다:

  1. Dispatchers.Default:
    • CPU 집약적인 작업을 위해 사용됩니다.
    • 기본적으로 코틀린이 관리하는 스레드 풀에서 실행됩니다.
    • 백그라운드에서 실행되는 CPU 중심의 작업에 적합합니다.
  2. Dispatchers.IO:
    • 파일 입출력, 네트워크 요청과 같은 입출력 작업에 최적화된 디스패처입니다.
    • 입출력 작업은 CPU 작업보다는 느리지만 자주 대기 상태에 있으므로, 스레드를 효율적으로 활용합니다.
  3. Dispatchers.Main:
    • 안드로이드나 UI 기반 애플리케이션에서 UI 스레드에서 코루틴을 실행하기 위한 디스패처입니다.
    • UI 작업을 처리할 때 사용되며, 안드로이드 환경에서 자주 사용됩니다.

간단한 예제

다양한 디스패처를 사용하여 코루틴이 어떻게 실행되는지 확인해보겠습니다.

import kotlinx.coroutines.*

fun main() = runBlocking {
    // Default Dispatcher
    launch(Dispatchers.Default) {
        println("Running on Dispatchers.Default: ${Thread.currentThread().name}")
        delay(1000L)
    }

    // IO Dispatcher
    launch(Dispatchers.IO) {
        println("Running on Dispatchers.IO: ${Thread.currentThread().name}")
        delay(1000L)
    }

    // Main Dispatcher (안드로이드 UI 스레드에서 사용)
    // launch(Dispatchers.Main) {
    //     println("Running on Dispatchers.Main: ${Thread.currentThread().name}")
    //     delay(1000L)
    // }

    println("Main program continues on: ${Thread.currentThread().name}")
}

설명

  1. Dispatchers.Default:
    • CPU 집약적인 작업을 처리하기 위한 디스패처로, 스레드 풀에서 실행됩니다.
    • "Running on Dispatchers.Default: DefaultDispatcher-worker-1" 같은 출력이 나옵니다.
  2. Dispatchers.IO:
    • 입출력 작업에 적합한 디스패처로, 입출력 대기 시간이 많은 작업에서 효율적으로 처리됩니다.
    • "Running on Dispatchers.IO: DefaultDispatcher-worker-2" 같은 출력이 나옵니다.
  3. Dispatchers.Main:
    • 이 디스패처는 안드로이드 앱의 UI 스레드에서 실행되도록 설계되었습니다. 데스크탑 환경에서는 사용되지 않으므로, 주석 처리했습니다.

출력 결과

Main program continues on: main
Running on Dispatchers.Default: DefaultDispatcher-worker-1
Running on Dispatchers.IO: DefaultDispatcher-worker-2

 

 

728x90
댓글