[kotlin] corountine context는 뭔가...

context란

코루틴이 실행되는 일련의 설정과 환경이라고 생각해보자. context는 코루틴에게 이렇게 말한다.

"이 코루틴이 실행되면 이렇게 동작하면 돼. 예를 들면 어떤 스레드를 쓸건지, 어떤 job에 속할 건지, 에러 때는 어떻게 할건지..."

 

context 구성요소

  • Dispatcher
    • 어떤 스레드나 스레드 풀을 사용할지 조정한다 (Dispatchers.IO, Main)
  • Job
    • 코루틴의 라이프 사이클(생명 주기)
  • CorountineName
    • 로그를 위한 디버그 명
  • ExceptionHandler
    • uncaught 에러를  처리하는 방법 (CorountineExceptionHandler)

기본 예제

val context = Dispatchers.IO + CorountineName("fetch-user")
launch(context) {
	println("Running in context: ${corountineContext[CorountineName]}")
}
  • 이 코루틴은 IO 스레드 풀에서 작동한다
  • 디버깅과 로깅을 위하여 "fetch-user"라고 명명함

context에 접근하기

launch {
    println(coroutineContext[Job])             // Job info
    println(coroutineContext[CoroutineName])   // Name (if set)
    println(coroutineContext[CoroutineDispatcher]) // Dispatcher
}

어느 코루틴이든 coroutineContext. 을 사용하여 접근을 할 수 있다.

context 실제 예제

@Controller
class MyController {

    @GetMapping("/user")
    suspend fun getUser(): ResponseEntity<String> = withContext(Dispatchers.IO) {
        val user = externalApi.fetchUser()  // non-blocking, but runs on IO thread
        ResponseEntity.ok(user.name)
    }
}

코루틴 context를 사용하여 일시적으로 IO 스레드 풀을 사용하도록 지시하는 예제이다. IO스레드풀은 blocking에 의해 스레드 속도가 방해될 때 사용한다.

// 무거운 연산에 최적화 된 작업을 할 때
CoroutineScope(Dispatchers.Default).launch {}

// UI 작업을 할 때
CoroutineScope(Dispatchers.Main).launch {}

// 네트워크, DB 작업 등에 최적화된 작업을 할 때
CoroutineScope(Dispatchers.IO).launch {}

 

Dispatchers.IO

1개의 스레드가 있고 3개의 코루틴이 있다고 가정해보자.

 

launch {
    delay(1000)
    println("A done")
}

launch {
    Thread.sleep(1000) // BAD
    println("B done")
}

launch {
    delay(1000)
    println("C done")
}

delay()는 non-blocking이다. 

Thread.sleep()는 blocking이다. 이 작업이 완료될 때까지 스레드를 꽁꽁 얼게한다.

즉, 코루틴B가 스레드를 block한다. 따라서 코루틴 A, C는 작업이 완료돼도 B를 기다려야한다.

 

이 때 사용할 수 있는 것이 Dispatcher.IO이다. blocking을 하는 작업들에 유용하게 쓰이는데...

launch(Dispatchers.IO) {
    Thread.sleep(1000) // Safe here
    println("Blocking done without affecting others")
}

즉 아래와 같은 코드는

withContext(Dispatchers.IO) {
    someBlockingCall()
}

"이 코드가 block할 것을 알고 있다, 따라서 blocking이 들어가는 것들은 백그라운드 스레드에서 진행하라. 메인 스레드는 내버려두고!" 라고 말하는 것과 같다.

 

withContext

CoroutineScope(Dispatchers.IO).async {
	delay(1000)
    withContext(Dispatchers.Main) {
    	// UI 작업
    }
}

withContext는 나 여기서는 이 실행환경으로 코루틴 돌릴래요~ 하는 것과 같다. (네이밍이 그래서 with context인듯)

위 코드를 보면 주황색으로 칠해진 부분은 Dispatcher.IO 환경에서 코루틴을 실행하고
초록색 부분에서는 Dispatcher.Main 환경에서 실행이 된다.
즉, 코루틴 실행 도중 실행 환경을 바꾸고 싶을 때 사용한다.

https://todaycode.tistory.com/183