코루틴 공식 가이드 자세히 읽기 — Part 2— Dive 1

Myungpyo Shim
4 min readMar 28, 2019

--

withContext 코루틴 빌더가 하는 일은 무엇인가?

Korean [English]

(목차로 돌아가기)

코루틴 공식 가이드의 코루틴 실행 취소 섹션에서 우리는 다음과 같이 finally {} 블럭에서 리소스 해제 등의 사유로 호출하는 중단함수에 대한 예제를 보았습니다.

18번 라인에 의해 job 이 취소될 경우 try { } 블럭은 CancelledException 이 발생시키고 finally { } 블럭으로 진입하게 됩니다. 이 때, withContext { } 없이 delay() 중단함수를 호출할 경우 현재 코루틴 컨텍스트는 이미 취소된 상태라서 또 다른 중단 함수를 호출할 수 없기 때문에 CancelledException 이 다시 발생하게 됩니다.

여기서 우리는 withContext { } 와 NonCancellable 컨텍스트 요소의 조합으로 이를 해결할 수 있습니다.

우선 withContext { } 는 어떤 일을 수행해주는 함수인지 살펴봅시다.

withContext 함수는 로직이 담긴 코드블럭{}과 이것이 실행 될 코루틴 컨텍스트를 함수의 파라미터로 받습니다.

suspendCoroutineUninterceptedOrReturn() 함수는 전달 된 코드 블럭에서 호출 코루틴(Continuation) 정보에 접근할 수 있도록 해줍니다. 또한, 전달 된 코드 블럭에서 COROUTINE_SUSPENDED 라는 미리 정의 된 값을 반환 할 경우에는 코루틴이 처리를 위해 시간이 필요하여 값을 바로 반환하지 않고 처리가 완료되면 continuation 파라미터를 통해 결과를 전달할 것임을 나타내고, 그 이외의 값을 반환할 경우에는 중단 없이 바로 결과 값을 반환한 것을 나타냅니다.

withContext 함수 안에서는 전달된 컨텍스트에 따라 크게 서로 다른 3가지 방식의 처리가 이루어집니다.

  • Function Caller — Callee 코루틴 컨텍스트가 동일한 경우
    - 현재 컨텍스트에서 그대로 코드 블럭(block)을 실행해도 무방함으로 ScopeCoroutine 을 만들어 바로 실행합니다. ScopeCoroutine 은 코루틴 간 컨텍스트가 동일하여 곧장 이어서 작업을 수행할 수 있을 경우 사용됩니다.
  • Function Caller — Callee 코루틴 컨텍스트가 서로 다른 부분을 가지고 있긴하지만 Dispatcher 컨텍스트 요소는 동일한 경우UndispatchedCoroutine 을 만들고 새로 만들어진 컨텍스트 안에서 실행합니다. (withContext)
  • 그 외의 경우에는 코루틴 컨텍스트간에 dispatcher 를 포함한 변경사항이있는 것이기 때문에 새로운 컨텍스트를 이용하여 적절한 스레드로 dispatch 되어 실행될 수 있도록 DIspatchedCoroutine 을 생성하여 실행합니다.

우리 예제에서 withContext로 넘긴 컨텍스트는 NonCancellable 이라는 객체 였습니다. 이는 다음과 같이 구현되어 있습니다. (일부만 발췌)

object 이므로 구현 객체이고 AbstractCoroutineContextElement 를 상속하므로 컨텍스트 요소입니다. 중요한 부분은 isActive 속성이 항상 true 를 반환하도록 구현되어 있다는 것입니다. 그렇기 때문에 취소된 컨텍스트에서 이 NonCancellable 컨텍스트로 전환하여 코루틴을 수행하면 취소 예외 없이 작업을 수행할 수 있게 됩니다.

그러면 withContext(NonCancellable) { } 은 위에서 설명한 withContext 의 3가지 수행 경로 중에 어느 경로로 진행되게 될까요?

우선, 현재 실행중인 컨텍스트가 NonCancellable 과는 다른 컨텍스트이므로 첫번째 경로는 아닙니다. 그렇다면 dispatcher 가 동일하여 두번째 경로로 선택되게 될까요? 정답은 “그렇다" 입니다. 코드에서 oldContext 는 현재 코루틴의 컨텍스트이고 newContext는 oldContext + NonCancellable 입니다. NonCancellable 에는 dispatcher 컨텍스트 요소에 대한 정의가 들어있지 않기 때문에 이 둘을 더하게 되면 oldContext 의 dispatcher 요소를 상속하여 사용하게 됩니다.

끝.

(목차로 돌아가기)

--

--