[Kotlin] Ticker(티커) 모드 차이(TickerMode.FIXED_PERIOD, FIXED_DELAY)
개발 환경: Kotlin 1.6
coroutines 라이브러리에는 **티커(ticker)**라고 하는 특별한 랑데부 채널이 있다. 이 채널은 Unit 값을 계속 발생시키되 한 원소와 다음 원소의 발생 시점이 주어진 지연 시간만큼 떨어져 있는 스트림을 만든다.
여기서 랑데부 채널이란 내부 버퍼가 없어 이 채널에서의 send() 호출은 다른 어떤 코루틴이 receive()를 호출할 때까지 항상 일시 중단된다. 마찬가지로 receive() 호출은 다른 어떤 코루틴이 send()를 호출할 때까지 일시 중단되는 특성을 가진다.
즉, 랑데부 채널은 생산자와 소비자 코루틴이 교대로 활성화되도록 보장한다.
티커 채널을 만들려면 ticker()라는 함수를 사용한다.
1 | import kotlinx.coroutines.* |
1 | null |
0ms ~ 50ms(타임아웃): 50ms(밀리초) 내에 티커 신호를 받으려고 시도하나, 티커 지연 시간이 100ms이므로 withTimeoutOtNull()은 신호를 받지 못하고 타임아웃이 걸려 널을 반환한다.
50ms ~ 100ms: 타임아웃이 1회 난 후 다음 60ms 안에 신호를 받으려고 시도한다. 그리고 이번에는 50 + 60ms가 100ms 보다 길기 때문에 결괏값을 얻는다. receive()가 호출되면 티커가 재개된다.
2_1. 100ms ~ 350ms: 이때 소비자 코루틴이 약 250ms 동안 일시 중단된다. 일시 중단으로부터 100ms 후에 티커는 다른 신호를 보내고 신호가 수신될 때까지 일시 중단된다. 그리고 소비자 코루틴과 티커 코루틴 모두 150ms 동안 일시 중단 상태로 남는다.
350ms: 소비자 코루틴이 재개되고 신호를 요청하려고 시도한다. 신호가 이미 보내졌기 때문에 receive()는 즉시 결과를 반환한다.
3_1. 이제 티커는 마지막 신호를 보내고 나서 얼마나 시간이 지났는지 검사하고(250ms), 지연 시간을 50ms로 줄인다.
350ms ~ 400ms: 소비자는 50ms 타임아웃 안에 신호를 받으려고 시도한다. 다음 신호가 50ms 이전에 보내졌기 때문에 이 시도는 거의 확실히 성공할 것이다.
400ms ~ 460ms(타임아웃): 마지막으로, 신호를 받으려는 receive() 호출이 거의 즉시 일어난다. 따라서 티커는 전체 지연 시간(100ms)를 다시 기다린다. 그 결과, 마지막 receive() 호출은 60ms 타임아웃 안에 티커로부터 신호를 받지 못하기 때문에 널을 받는다.
티커 모드를 FIXED_DELAY로 고정하면 결과가 다음과 같이 바뀐다.
1 | null |
초반부는 앞의 예제와 비슷하게 진행된다. 하지만 250밀리초의 긴 지연 이후 소비자 코루틴이 재개될 때부터는 동작이 달라진다.
350ms: 소비자 코루틴이 재개되고 신호를 요청하려고 시도한다. 신호가 이미 보내졌기 때문에 receive()는 즉시 결과를 반환한다.
3_1. receive()로 결과를 넘긴 시점에서 티커는 현재시간을 고려하지 않고 여기서부터 100ms를 다시 기다린다.
350ms ~ 410ms(타임아웃): 티커가 신호를 보내려면 40ms 남았으므로 널을 받는다.
410ms ~ 450ms: 3_1에서 티커가 재개된 시간으로부터 100ms가 지났으므로 결과를 무사히 반환받는다.
참고
알렉세이 세두노프 <코틀린 완벽 가이드>
[Kotlin] Ticker(티커) 모드 차이(TickerMode.FIXED_PERIOD, FIXED_DELAY)
https://dl137584.github.io/2022/04/30/023-ticker-in-coroutines-library/