본문 바로가기
공부/코틀린코루틴:딥다이브(마르친 모스카와)

9장 - 취소

by 띵커베르 2024. 2. 5.
728x90
  •  코틀린 코루틴에서 아주 중요한 기능 중 하나는 바로 취소(cancellation) 이다.
  • 기본적인 취소
    • Job 인터페이스는 취소하게 하는 cancel 메서드를 가지고 있습니다.
    •  
    • cancel 메서드를 호출하면 다음과 같은 효과를 가져올 수 있다.
      • 호출한 코루틴은 첫 번째 중단점에서 잡을 끝냅니다.
      • 잡이 자식을 가지고 있다면, 그들 또한 취소됩니다.하지만 부모는 영향을 받지 않는다.
      • 잡이 취소되면, 취소된 잡은 새로운 코루틴의 부모로 사용될 수 없다. 취소된 Job 은 Cancelling 상태가 되었다가 Cancelled 상태로 바뀝니다.
@Test
fun `98페이지`() {
    runBlocking {
        val job = launch {
            repeat(1_000) { i ->
                delay(200)
                println("Printing $i")
            }
        }

        delay(1100)
        job.cancel()
        job.join()
        println("Cancelled successfully")
    }
}

// Printing 0
// Printing 1
// Printing 2
// Printing 3
// Printing 4
// Cancelled successfully

 

  • 코루틴을 취소하기 위해서 사용되는 예외는 cancellationException 이어야 하기 때문에 인자로 사용되는 예외는 반드시 cancellationException 의 서브타입이어야 한다
    • 코루틴은 내부적으로 cancellationException 을 취소 메커니즘으로 사용.
    • 이 예외들은 모든 핸들러에게 무시되므로, 추가적인 디버그 정보의 소스로만 사용되어야 한다.
    • 예외를 catch 블록에서 캐치할 수 있다.
      • CancellationException 은
    • Job.cancel 을 사용하여 취소되며, 코루틴은 종료되지만 부모 코루틴은 취소하지는 않는다.
      • job.cancel() 메서드를 호출하면 코루틴은 CancellationException 을 받습니다.
      • 해당 예외는 코루틴이 취소되었음을 나타내고, 코루틴은 다음 일시 중단 지점에서 멈추게 된다.
    • 에러 처리와 차이점
      • CancellationException의 경우 코루틴의 정상적인 종료 부분으로 초리된다. 코루틴은 이 예외를 에러로 취급하지 않고, 코루틴의 실행을 멈춘다.
    • 위 의 내용을 바탕으로 서브타입을 인자로 사용되는 예외는 CancellationException 의 서브타입이어야 한다.
    • cancel 이 호출된 뒤 다음 작업을 진행하기 전에 취소 과정이 완료되는 걸 기다리기 위해 join 을 사용하는 것이 일반적이다.
      • join 을 호출하지 않으면 레이스 컨디션 상태가 될 수 있다.
    • cancel 과 join 을 함께 호출할 수 있는 간단한 방법으로는 cancelAndJoin 이라는 확장 함수를 제공한다.
    • 장 함수를 제공한다.
  • 취소는 어떻게 작동하는가?
    • 잡이 취소되면 Cancelling 상태로 바뀝니다.
    • 상태가 바뀐 뒤 첫 번째 중단점에서 CancellationException 예외를 던집니다.
      • 예외는 try-catch 구문을 사용하여 잡을 수도 있지만 다시 던지는 것이 좋습니다.
        • CancellationException 을 다시 던지면 코루틴이 취소된 것을 시스템이 알아차리고, 다른 코루틴들도 이를 인지할 수 있다.
        • CancellationException 은 올바르게 코루틴이 제대로 끝났다라는 신호이다. 다시 던진다면 코루틴이 문제 없이 종료되었다라는 것을 확실히 할 수 있다.
        • CancellationException 을 다시 던짐으로써 finally 블록이나 코루틴의 일시 중단 함수들은 여전히 실행될 수 있다.이를 통해 리소스를 적절히 해제할 수 있다.
    • 취소된 코루틴이 단지 멈추는 것이 아니라 내부적으로 예외를 사용해 취소되는 걸 명심해야 한다.
  • 취소 중 코루틴을 한 번 더 호출하기
    • 코루틴이 실제로 종료되기 전에
    • CancellationException 을 잡고 좀더 많은 연산을 수행할 수 있다.
    • withContext 내부에서는 취소될 수 없는 Job인 NonCancellable 객체를 사용합니다. 따라서 블록 내부에서 잡은 액티브 상태를 유지하며, 중단 함수를 원하는 만큼 호출할 수 있다.
    • 간단히 말해서 코루틴이 취소되더라도 필요한 정리 작업을 수행하려면 withContext(NonCancellable) 을 사용하여 취소되지 않는 특정 영역을 만들고, 그안에서 필요한 작업을 수행하면 된다.
  • invokeOnCompletion
    • 자원을 해제하는 데 자주 사용되는 또 다른 방법은 Job 의 invokeOnCompletion 메서드를 호출하는 것이다.
    • invokeOnCompletion 메서드는 잡이 Completed 나 Cancelled 같은 마지막 상태에 도달했을 때 호출될 핸들러를 지정하는 역할을 한다.
  • 중단될 수 없는 걸 중단하기
    • 취소는 중단점에서 일어나기 때문에 중단점이 없으면 취소를 할 수 없습니다.
    • Thread.sleep() 은 안좋다
      • 스레드를 블로킹한다.
      • 취소가 불가능하다
      • 응답성이 저하된다.
    • yield()
      • yield() 를 호출하면 현재 코루틴은 일시중지되며 이는 코루틴이 더 이상 실행 중이지 않음을 의미한다.
      • 이는 스레드가 블록되는 것과는 다르다 코루틴은 일시 중지 상태가 되지만 실행 중이던 스레드는 다른 작업을 계속 수행할 수 있다.
      • 현재 코루틴이 작업을 잠시 중단하고, 스케줄러에게 제어를 넘깁니다.스케줄러는 이후에 다른 코루틴에게 실행 기회를 제공할 수 있다.
    • 중단 가능하지 않으면서 CPU 집약적이거나 시간 집약적인 연산들이 중단 함수에 있다면, 각 연사들 사이에 yield 를 사용하는 것이 좋다
      • yield() 함수를 사용하는 주된 이유는 애플리케이션의 응답성과 자원의 공정한 분배를 향상시키기 위해서이다.
      • 응답성 향상
        • 멀티태스킹 환경에서 한 태스크가 CPU 를 장시간 독점하면, 다른 태스크들은 실행될 기회를 잃게되고, 이는 전체 시스템의 반응 시간을 늘릴 수 있다.
        • yeild() 를 사용하면, CPU 집약적 작업을 수행하는 코루틴이 잠시 멈추고 다른 코루틴이 실행될 수 있는 기회를 준다. 이는 코루틴이나 태스크 간에 자원을 공유할 때 전체적인 응답성을 향상시킬 수 있다.
      • 자원의 공정한 분배
        • 여러 코루틴이 동시에 실행되는 환경에서, 모든 코루틴이 CPU 시간을 공정하게 받지 못하면, 일부 코루틴이 지나치게 오래 기다려야할 수 있다.
      • 작업의 미세 조정
        • yield() 는 코드 내에서 코루틴의 실행을 조정할 수 있는 수단을 제공한다 예를 들어 대규모 데이터를 처리하는 작업에서 각 단계마다 yield() 를 호출하면 다른 코루틴도 중간중간 실행될 기회를 가질 수 있다.
        • 이런 방식으로 yield() 를 사용하면 한 코루틴이 시스템의 모든 리소스를 독점하는 것을 방지하고, 작업을 좀 더 세밀하게 조정할 수 있어 전체적인 시스템의 효율성과 안정성을 높일 수 있다.
      • ensureActive
        • yield 와 비슷하지만 둘은 매우 다르다
        • 함수는 현재 코루틴의 상태가 여전히 활성화되어 있는지 검사합니다. 만약 코루틴이 이미 취소되었다면, ensureActive()는 CancellationException을 던집니다.
        • 사용 사례: 장시간 실행되는 작업에서 정기적으로 코루틴이 취소되었는지 확인하고 싶을 때 사용합니다. 이를 통해 불필요한 작업을 중단하고, 코루틴의 자원을 빠르게 해제할 수 있습니다.
      • yield vs ensureActive
        • 실행 양보 vs 상태 검사: yield는 현재 코루틴이 실행을 양보하는 데 중점을 둡니다. 반면, ensureActive는 코루틴의 상태를 검사하고, 코루틴이 취소되었는지 확인하는 데 사용됩니다.
        • 사용 시점: yield는 다른 코루틴에게 실행을 공정하게 양보하고자 할 때 주로 사용되며, ensureActive는 코루틴이 장시간 작업을 수행하는 동안 여전히 활성 상태인지 확인하고자 할 때 사용됩니다.
    • suspendCancellableCoroutine
      • 코루틴에서 취소 가능한 비동기 작업을 수행할 때 사용함
      • 코틀린 코루틴 라이브러리 일부로, 기존의 콜백 기반 API 나 다른 비동기 작업을 코루틴과 호환되게 래핑하는데 유용하다.
      • suspendCancellableCoroutine 은 코루틴을 일시 중단하고, 외부의 비동기 작업이 완료될 때까지 기다린다. 비동기 작업이 완료되면 결과를 반환하거나 예외를 던질 수 있다.
      • 취소가능: 취소를 지원한다. 취소되면 suspendCancellableCoroutine 블록내에서 등록한 취소 핸들러가 호출된다.
728x90

댓글