본문 바로가기

KOTLIN

[KOTLIN IN DEPTH] 구조적 동시성과 코루틴 문맥

코루틴 영역과 구조적 동시성

코루틴은 기본적으로 전역 영역(Global Scope)에서 실행됩니다. 이는 코루틴의 생명주기가 어플리케이션 전체의 생명주기에 의해 제약되는 것을 의미합니다. 그러나 때로는 특정 연산을 수행하는 동안에만 코루틴이 실행되길 원할 수 있습니다. 이를 위해 특정한 코루틴 영역을 만들어 사용할 수 있습니다.

 

구조적 동시성은 부모-자식 관계를 가진 코루틴이 서로 연관되어 실행되는 개념입니다. 특정 코루틴을 다른 코루틴의 문맥에서 실행하면 부모-자식 관계가 형성되어 자식의 실행이 모두 완료되어야 부모가 끝날 수 있습니다.

import kotlinx.coroutines.*
import java.lang.System.*

suspend fun main() {
    
    runBlocking{
        println("Parent task started")
    
        launch{
            println("Task A started")
            delay(100)
            println("Task A finished")
        }

        launch{
            println("Task B started")
            delay(100)
            println("Task B finished")
        }

        delay(100)
        println("Parent task finished")
    
    }
    println("Shutting down...")
}

// 결과
// Parent task started
// Task A started
// Task B started
// Parent task finished
// Task A finished
// Task B finished
// Shutting down...

이 코드는 runBlock{} 으로 최상위 코루틴을 시작하고 현재 CoroutineScope 인스턴스 안에서 launch를 호출해 두 개의 자식 코루틴을 시작합니다. 결과를 살펴보면 runBlocking()이 메인 스레드를 블럭하고 있기 때문에 부모 스레드가 끝나야 메인 스레드의 블럭이 풀리고 마지막 메시지가 출력됩니다. 

 

또한 coroutineScope() 호출로 코드 블럭을 감싸면 커스텀 영역을 도입할 수 있습니다. coroutineScope는 자식들이 완료되기 전까지 종료 되지 않습니다. runBlocking과 유사하지만 가장 큰 차이는 현재 스레드를 Block 시키지 않는다는 점입니다. 아래 예제는 두 자식 코루틴의 실행이 끝날 때 까지 coroutineScope 호출이 일시 중단되므로 "Custom Scope finished" 메시지는 마지막에 표시됩니다. 

import kotlinx.coroutines.*
import java.lang.System.*

suspend fun main() {
    
    runBlocking{
        println("Custom Scope started")
    
        coroutineScope{
            launch{
                delay(100)
                println("Task 1 finished")    
            }
            launch{
            	delay(100)
            	println("Task 2 finished")
        	}
        }
        println("Custom Scope finished")
    
    }
}
// 결과
// Custom Scope started
// Task 1 finished
// Task 2 finished
// Custom Scope finished

 

코루틴 문맥 (Coroutine Context)
코루틴 문맥(Coroutine Context)은 코루틴이 실행되는 환경에 관련된 정보를 담고 있는 개념입니다. 코루틴은 항상 특정 문맥에서 실행되며, 이 문맥은 CoroutineContext 객체에 의해 정의됩니다. CoroutineContext는 키-값 쌍의 불변 컬렉션으로, 다양한 정보와 설정이 들어있습니다. 코루틴 문맥의 중요한 부분 중 하나는 스레드 풀을 나타내는 것입니다. 기본적으로 CommonPool이라는 공통 스레드 풀이 지정되어 있어 코루틴이 실행될 때 이 풀에서 스레드를 가져와 작업을 수행합니다.

 

또한, CoroutineContext는 여러 가지 다른 속성도 가질 수 있습니다. 예를 들어, 예외 처리, 부가적인 정보, 그리고 다른 문맥을 결합하는 기능 등이 있습니다. 아래 코드는newSingleThreadContext를 사용하여 새로운 스레드 컨텍스트를 만들고

+ 연산자를 통해 여러 속성을 결합하여 사용했습니다.

코루틴 문맥은 코루틴이 실행되는 환경을 제어하고 조절하는데 중요한 역할을 합니다.

import kotlinx.coroutines.*
import java.lang.System.*

// 예시: CoroutineContext의 여러 속성
import kotlinx.coroutines.*

suspend fun main() {
    runBlocking {
        val customContext = newSingleThreadContext("MyThread")
        
        launch(customContext + CoroutineName("MyCoroutine")) {
            println("Running on ${Thread.currentThread().name}")
        }
    }
}

// 결과 : Running on MyThread @MyCoroutine#2