Kt.Academy Kotlin Coroutines Deep Dive Summary 2부 — 코루틴 컨텍스트 및 취소

GodDB
6 min readMar 22, 2022

--

본 게시글은 Kt.Academy의 Kotlin Coroutines DEEP DIVE의 요약본입니다.

목차

  1. 코루틴 원리 및 빌더
  2. 코루틴 컨텍스트 및 취소 — 현재 게시글
  3. 코루틴 예외 처리 및 스코프 함수
  4. 코루틴 디스패처 및 스코프 함수
  5. 코루틴 테스트
  6. Flow 빌더 및 기본 Operator
  7. Flow Operator

CoroutineContext란?

코루틴 빌더를 보면 CoroutineContext가 있습니다.

CoroutineContext는 코루틴이 실행될 환경을 정의하기 위한 인터페이스로, Job, CoroutineName, CoroutineDispatcher 등이 CoroutineContext의 구현체입니다.

CoroutineContext에 요소 찾기

CoroutineContext는 Map과 같은 컬렉션 처럼 다룰 수 있습니다.

CoroutineContext 병합

여러 Context를 병합할 경우가 있습니다. 그럴 땐 + operator 연산자를 통해 병합할 수 있습니다.

또한 Map과 같이 중복된 Element를 담을 경우 대체 됩니다.

CoroutineContext 및 Builder

기본적으로 부모의 CoroutineContext을 자식에게 상속합니다.

또한 CoroutineContext를 덮어 쓸 수도 있습니다.

Coroutine 취소하기

코루틴을 취소하는 방법은 Job을 이용해서 취소 할 수 있습니다.

JobCoroutineContext의 구현체이며, Job을 이용해서 cancel() , join() 등을 통해 코루틴을 제어 할 수 있습니다.

cancel()을 호출한다고 무조건 코루틴이 즉시 종료되는 것은 아닙니다. suspend 함수를 만나야만 종료됩니다.

그렇기 때문에 코루틴이 완전히 cancel된 시점과 동기화를 맞추기 위해서

cancelAndJoin()을 통해 코루틴이 완전히 종료될 때 까지 일시정지를 시킬 수 있습니다.

또한 코루틴 빌더 함수에 Job을 담아서 취소 시킬 수도 있습니다. 이 경우엔 한번에 많은 코루틴을 일괄 종료 시키기 위해서 사용합니다.

또한 scope를 이용해서 종료할 수 있습니다.

Job을 주입하는 것과 리턴된 Job은 무엇이 다를까?

우리가 Job을 얻는 방법은 2가지 입니다.

  • Job()을 통해 인스턴스 생성 후, 빌더 함수에 담는다.
  • launch()가 리턴하는 Job

이 두개의 차이점은 전체 종료하냐, 하나만 종료하냐 입니다.

job 객체를 코루틴 빌더 함수에 담을 경우 자식 코루틴이나, 해당 job을 받은 다른 코루틴들이 일괄 종료하게 됩니다.

launch함수를 통해 리턴된 job은 해당 코루틴만을 종료합니다.

또한 해당 코루틴에게 자식 코루틴이 있다면 자식 코루틴도 종료됩니다.

취소는 어떻게 이뤄지나?

Jobcancel()하면 Job의 상태는 취소 상태로 변경됩니다. 그리고 CancellationException을 던집니다. 코루틴 내부에서는 이 CancellationException을 try-catch 처리해서 취소를 반영합니다.

그렇기 때문에 catch를 통해 CancellationException을 처리해서는 안됩니다.

만약 Cancel에 대한 시점을 알고 싶다면 job.invokeOnCompletion()를 통해서 처리할 수 있습니다.

만약 Exception이 발생할 우려가 있고, 그 Exception이 어떤게 터질지 모른다면 , CancellationException만 예외로 다시 throw시켜서 처리할 수 있습니다.

Job이 취소된 상태에 다시 코루틴을 생성한다면?

Jobcancel()하면 Job의 상태값은 취소상태입니다. 이 상태는 변경되지 않으며, 이 상태에서 다시 코루틴을 생성 하면 무시됩니다.

하지만 Job이 취소 되었을 때, 트랜젝션 관리를 위해서 코루틴을 실행해야 할 수 있습니다.

그럴 경우에는 NonCancellable Job을 이용해서 처리 할 수 있습니다.

취소할 수 없는 코루틴을 취소하기

Jobcancel()한다고 해서 무조건 그리고 즉시 cancel 되는 것은 아닙니다.

Job을 취소하려면 suspend 함수를 만나야 취소 될 수 있습니다. 그렇기 때문에 cancel() 했을 때, 즉시 취소 되어야 한다면 그 부분의 앞에서 suspend 함수를 호출 시키게 하거나, isActive로 상태 체크 혹은 ensureActive()로 처리할 수 있습니다.

위와 같이 suspend 함수가 없다면 이 무한루프에 벗어날 수 없습니다.

하지만 이렇게 중간에 yield()와 같은 suspend 함수를 넣는다면 무한루프에 벗어날 수 있습니다.

사실 여기서 가장 좋은 방법은 ensureActive()를 사용하는 것 입니다.

만약 active 상태가 아니라면 CancellationException을 throw 시켜, 즉시 코루틴을 취소 시킵니다.

suspendCancellationableCoroutine

앞서 1부에서 suspendCoroutine() 를 통해서 콜백을 코루틴으로 변환할 때 사용한다고 말씀드렸습니다. suspendCancellationableCoroutine() 도 역시 주로 콜백을 코루틴으로 변환하기 위해서 사용하며,

suspendCoroutine 와의 다른점은 취소가 가능하다는 점입니다.

또한 취소가 가능하므로, Continuation.invokeOnCancellation()을 통해 코루틴이 취소 되었을 때 등록된 콜백을 제거 시켜 오류를 방지할 수 있습니다.

--

--