코틀린의 코루틴 — 2. Cancellation and Timeouts

hongbeom
hongbeomi dev
Published in
5 min readJun 5, 2020

코루틴의 취소 및 시간 초과에 대해 알아봅니다

Photo by Icons8 Team on Unsplash

이 글은 공식 코루틴 가이드 링크의 내용을 기반으로 하여 작성하였습니다. 이 글을 작성할 당시 버전은 1.3.71입니다.

1. Coroutine Basic2. Cancellation and Timeouts3. Composing Suspending Functions4. Coroutine Context and Dispatchers5. Asynchronous Flow 1부6. Asynchronous Flow 2부7. Channels

취소와 시간초과

이번 글에서는 코루틴의 취소와 시간초과에 대해 알아봅니다.

코루틴의 실행 취소

장기간 실행되는 애플리케이션에서는 백그라운드 코루틴을 세밀하게 제어해야 합니다. 예를 들어, 사용자는 코루틴을 시작한 페이지를 닫았을 수 있고, 이제 그 결과는 더 이상 필요하지 않고 그 동작은 취소될 수 있어야 합니다. 이런 경우를 위해 launch 함수는 실행 중인 코루틴을 취소할 수 있는 job을 반환합니다.

main()job.cancel을 호출하는 즉시, 다른 코루틴이 취소되었기 때문에 우리는 그 이후 어떤 출력도 볼 수 없습니다. canceljoin호출을 결합한 Job 익스텐션인 cancelAndJoin 함수도 있습니다.

Cancellation is cooperative

코루틴의 취소는 굉장히 협조적입니다. kotlinx.coroutine의 모든 suspend 함수는 취소할 수 있습니다. 이들은 코루틴의 취소를 확인한 후 CancellationException을 던집니다. 단, 코루틴이 연산 작업 중이고 취소 여부를 확인하지 않는다면, 다음 코드처럼 취소할 수 없습니다.

연산 코드 취소 가능하도록 만들기

연산 코드를 취소할 수 있게 만드는 방법에는 두 가지가 있습니다. 첫 번째는 취소를 확인하는 정지 기능을 주기적으로 발동시키는 것입니다. 이를 위한 좋은 선택지인 yield 함수가 있습니다. 다른 하나는 취소 상태를 명시적으로 확인하는 것입니다. 후자의 접근법을 시도해보겠습니다.

이전 예에서 while (i < 5)while (isActive)로 교체하고 다시 실행해보겠습니다.

보시다시피, 이제 연산 루프는 취소되었습니다. isActiveCoroutineScope 객체를 통해 코루틴 내부에서 사용할 수 있는 익스텐션 프로퍼티입니다.

Finally와 함께 Resource close

Cancellable한 suspend 함수는 취소 시 CancellationException을 던지므로 통상적인 방법으로 처리할 수 있습니다. 예를 들어 try {...} finally {...} 식이나 코틀린의 use 함수는 코루틴이 취소될 때 정상적으로 최종 작업을 진행합니다.

Non-cancellable block

이전 예제에서 코드를 실행하는 코루틴이 취소되기 때문에 finally 블록에서 suspend함수를 사용하려고 하면 CancellationException 이 발생합니다. 일반적으로 닫는 작업(파일 닫기, 작업 취소 또는 통신 채널의 닫기)은 일반적으로 차단되지 않고 어떠한 기능도 동반하지 않기 때문에, 이것은 문제가 되지 않습니다. 그러나 취소된 코루틴에서 suspend 함수를 사용해야 하는 드문 경우, 해당 코드를 withContext(NonCancellable) {…} 로 감싸서 사용할 수 있습니다. 아래 코드처럼 말입니다.

시간초과

코루틴의 실행을 취소하는 가장 보통적인 이유는 코루틴 실행 시간이 일정 시간 제한을 초과했기 때문인 이유가 많습니다. 해당하는 job에 대한 참조를 수동으로 감지하고 별도의 코루틴을 실행하여 지연시킨 후 감지한 코루틴을 취소할 수도 있지만, Timeout을 사용하여 간단하게 사용할 수도 있습니다. 다음 코드를 확인해봅시다.

TimeoutCancellationExceptionwithTimeout에서 던져지는 CancellationException의 하위 클래스입니다. 우리는 이 익셉션이 콘솔에 인쇄된 것을 본 적이 없습니다. 왜냐하면 취소된 코루틴의 CancellationException 내부에 존재하기 때문입니다. 이 익셉션은 코루틴이 완료되었다는 정상적인 신호로 간주됩니다. 그러나 위의 코드에서는 main 함수안에서 withTimeout이 사용되었습니다.

취소는 익셉션에 불과하기 때문에 모든 자원은 보통 닫히게 됩니다. 우리는 코드를 timeout과 함께 try {...} catch (e: TimeoutCancellationException) {...} 블록으로 감싸서 특정 타입의 시간 초과에 대해 특별한 추가적인 작업을 처리 할 수도 있고, withTimeout과 유사하지만 timeout 시 null을 반환하는 withTimeoutOrNull을 사용하여 처리할 수 도 있습니다.

읽어주셔서 감사합니다🙌

다음 글에서 Suspend 함수의 구성이라는 주제로 이어서 작성하겠습니다.

--

--