반응형

Coroutine

Coroutine이란?

  • 코루틴은 비동기 프로그래밍을 위한 도구로서, 코드를 비동기적으로 실행할 수 있도록 도와주는 라이브러리를 말한다.
  • 코루틴은 기본적으로 경량 스레드와 비슷한 동작을 하지만, 스레드보다 적은 리소스를 소비하고 더 효율적으로 관리될 수 있다

스레드로 여러 작업 처리

  • 동시에 여러 작업이 필요할 때 각 작업별로 스레드를 생성하여 각각의 스레드 위에서 처리하도록 할 수 있다.
  • A 스레드의 작업 도중 B 스레드로 컨텍스트 스위칭이 중간에 발생할 수 있다
  • 컨텍스트 스위칭 과정은 무겁고, 리소스 사용권이 B 스레드에게 넘어갈 경우 A 스레드는 작업을 진행하지 못하여 리소스를 낭비하는 경우가 생긴다.

코루틴으로 여러 작업 처리

  • 코루틴은 각 작업별로 코루틴을 생성하여 처리하도록 한다.
  • 각 코루틴은 최소한으로 생성된 스레드 풀 위에서 동작한다.
  • 컨텍스트 스위칭 과정이 최소화 되기 때문에 성능상 이점이 있다.

Coroutine Scope

Coroutine Scope란?

  • 코루틴이 실행되는 범위를 말한다.

GlobalScope

  • 애플리케이션이 실행될 때 생성하여 사용하는 스코프(Singleton)
  • 메모리 누수의 원인이 될 수 있기 때문에 신중히 사용해야한다.
GlobalScope.launch {
    // task
}

CoroutineScope

  • 필요할 때 생성하여 사용하는 스코프
CoroutineScope(Dispatchers.IO).launch {
    // task
}

Coroutine Builder

Coroutine Builder란?

  • 코루틴을 시작하는 방법을 제공하는 함수를 말한다.

launch

  • 작업을 시작하고 결과를 반환하지 않는 함수
 GlobalScope.launch {
     // task
 }

async

  • 작업을 시작하고 결과를 Deffered 타입으로 반환하는 함수
val deferred = GlobalScope.async {
    "결과값"
}

val result = deferred.await() // 계산이 끝날 때까지 기다린 후 값 가져오기

runBlocking

  • 호출한 스레드를 블록킹하면서 코루틴을 실행하고 결과를 반환하는 함수
  • 일반적으로 사용하지 않고 주로 테스트나 메인 함수에서 사용된다.
runBlocking { 
    // task
}

withContext

  • 이미 실행 중인 코루틴 내부에서 컨텍스트를 변경하는데 사용하는 함수.

코드

GlobalScope.launch(newFixedThreadPoolContext(1, "mainThread")) {
    println("[${Thread.currentThread().name}] start")

    val result = withContext(Dispatchers.IO) {
        delay(1000)
        println("[${Thread.currentThread().name}] io")
        "Hello World"
    }

    println("[${Thread.currentThread().name}] result - $result")
}

출력

[mainThread] start
[DefaultDispatcher-worker-1] io
[mainThread] result - Hello World

withTimeout

try {
    withTimeout(1000L) { // timeout 시간을 밀리초 단위로 설정 (여기서는 1초)
        repeat(1000) { i ->
            println("Some work $i ...")
            delay(500L)
        }
    }
} catch (e: TimeoutCancellationException) {
    println("Timed out with exception: ${e.message}")
}

withTimeoutOrNull

val result = withTimeoutOrNull(1000L) { // timeout 시간을 밀리초 단위로 설정 (여기서는 1초)
    repeat(1000) { i ->
        println("Some work $i ...")
        delay(500L)
    }
    "Done" // 정상적으로 완료됐을 때 반환되는 값
}

println("Result is $result")

Coroutine Context

Coroutine Context란?

  • 코루틴의 동작과 상태를 결정하는 속성.
  • 코루틴 컨텍스트에서 가장 중요한 두 가지 요소는 Job과 Dispatcher이다.
  • Job: 코루틴의 생명주기를 관리
  • Dispatcher: 코루틴이 어느 스레드에서 실행될지 결정
GlobalScope.launch(Dispatchers.Default + CoroutineName("testCoroutine")) { // 컨텍스트에 디스패처와 이름을 추가
    println("[${Thread.currentThread().name}] coroutineName: ${coroutineContext[CoroutineName]?.name}, isActive: ${coroutineContext.isActive}")
}
val job = GlobalScope.launch {
    delay(1000)
    println("job")
}

job.cancel() // job 종료 가능
val threadLocal = ThreadLocal<String>()
threadLocal.set("Hello")

println("[${Thread.currentThread().name}] main start - ${threadLocal.get()}") // Hello

GlobalScope.launch(Dispatchers.Default + threadLocal.asContextElement(value = "World")) {
    println("[${Thread.currentThread().name}] coroutine - ${threadLocal.get()}") // World
}

println("[${Thread.currentThread().name}] main end - ${threadLocal.get()}") // Hello

Coroutine Dispatcher

Coroutine Dispatcher란?

  • 코루틴이 어느 스레드에서 실행될지 결정하는 역할.
  • 컨텍스트의 종류 중 하나.

Dispatchers.Default

  • 동시 작업 가능한 최대 갯수는 CPU의 코어 수와 같다.
  • 대기시간이 없고 지속적으로 CPU 작업을 필요로하는 무거운 작업에 적합하다.(ex. 파싱 작업, 복잡한 연산 등)
CoroutineScope(Dispatchers.Default).launch {
    // task
}

Dispatchers.IO

  • 필요에 따라 추가적으로 스레드를 더 생성하여 동시 작업이 가능하다.(최대 64개까지 생성 가능)
  • 대기시간이 긴 작업을 필요로하는 무거운 작업에 적합하다.(ex. 네트워크 통신, 파일 입출력 등)
CoroutineScope(Dispatchers.IO).launch {
    // task
}

newFixedThreadPoolContext

  • 사용자가 원하는 스레드의 수를 지정함으로써 동시에 실행할 코루틴의 최대 수를 제한할 수 있다.

예제 코드

// val dispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()
val dispatcher = newFixedThreadPoolContext(2, "myDispatcher")

repeat(5) {
    CoroutineScope(dispatcher).launch {
        println("[${Thread.currentThread().name}] Hello World")
    }
}

// 사용이 끝났을 때, 스레드 풀을 반드시 종료
dispatcher.close()

출력

[myDispatcher-1] Hello World
[myDispatcher-2] Hello World
[myDispatcher-1] Hello World
[myDispatcher-2] Hello World
[myDispatcher-1] Hello World

참고 링크

반응형

+ Recent posts