본문 바로가기

[Kotlin] 코루틴 #5 - Channel (채널)

코루틴 #5 - Channel (채널)

값의 흐름을 전송하는 Channel(채널)에 대해 작성

 

Coroutine 이전 글

  1. 코루틴 #1 - 기본
  2. 코루틴 #2 - CoroutineContext와 CoroutineScope란?
  3. 코루틴 #3 - suspend Function (중단함수)
  4. 코루틴 #4 - CoroutineBuilder와 ScopeBuilder

 

 

Channel (채널)

Channel은 송신자(SendChannel)와 수신자(RecevieChannel) 사이의 통신을 위한 비차단 요소

Java의 BlockingQueue와 유사하지만 중요한 차이점은
         BlockingQueue - put(), take() 차단(Blocking)해서 전송 / 수신하는 방법
         Channel - send(), receive() 차단이 아닌 일시중단(suspend)으로 전송 / 수신하는 방법

 

fun main() = runBlocking {
val channel = Channel<Int>()
launch {
// 1~5까지 Channel을 통해 전송, send() 
for (x in 1..5) channel.send(x * x)
}
// 5번 반복하며 Channel을 통해 수신, receive()
repeat(5) { println(channel.receive()) }
println("Done!")
}

 출력 결과

1
4
9
16
25
Done!

 

Channel은 Queue와 다르게 더 이상 요소(Element, Value)가 없다는 것을 나타낼 수 있습니다.

즉, Receive측에서 단순히 for-loop만으로 Channel의 모든 요소를 편리하게 순회할 수 있다는 의미

   Queue는 마지막 요소를 알 수 없기 때문에, 사전에 정의된 횟수를 알고 있어야함

// 전송자(Sender), 5번 전송
for (i in 1..5) channel.send(i * i)

// 수신자(Receiver), 5번 반복 수신
repeat(5) {
    println(channel.receive())
}


   Channel은 마지막 요소를 알 수 있기 때문에, 단순 for루프로 순회가 가능

// 전송자(Sender), 5번 전송
for (i in 1..5) channel.send(i * i)

// 수신자(Receiver), for 루프
for(msg in channel) { println(msg) }


어떻게 마지막 요소를 알 수 있을까?    'close() 메서드'


채널에는 close() 닫기 메서드가 있는데, 이 메서드는 호출 시 Channel을 통해 특별한 '닫기 토큰'을 전송

수신자의 for루프에서 이 특별한 '닫기 토큰'을 수신하면 반복이 중지되므로 이전의 모든 전송요소들이 보장된다

  

cancel() / close() 차이

채널에는 닫기(close)와 취소(cancel) 메서드가 존재, 서로 비슷해 보이지만 다르므로 차이를 알아야 합니다

두 메서드의 차이점

    close() - 채널 닫기 의미로, 이전에 보낸 요소들의 전송을 보장한다
                  즉, 수신자(Receiver)가 마지막 보낸 요소까지 수신을 하고 종료될 수 있다는 의미

    cancel() - 취소 의미로, 채널을 닫고 버퍼링된 전송한 요소들도 모두 제거
                  즉, 수신자(Receiver)는 마지막 전송 요소까지 수신할 수 없다는 의미

 

전송 메서드 'sned() / offer()'

다른 블로그들의 channel 예제를 보면서, 어떤 분은 offer를 사용 / 어떤 분은 send()를 사용하는 걸 보고 무슨 차이인가 싶어졌다

두 메서드 모두 SendChannel에서 ReceiveChannel로 요소를 전송하는 메서드이지만,
코루틴적으로 중요한 차이점이 존재 - suspend(중단) 유무

send() 메서드 구조

abstract suspend fun send(element: E): Unit

메서드를 보면 suspend(중단)와 반환값이 Unit인 점이 중요하게 봐야할 점이다

    1) channel의 버퍼가 가득 찼거나 존재하지 않을 경우 호출자를 일시중단(suspend)
 

    2) channel의 버퍼가 비었으면 요소를 전송

    3) channel이 닫힌(close) 경우, Send예외 발생

일반적으로 channel의 버퍼크기(capacity)는 0으로 설정된다. 즉 1전송 - 1수신 후 다시 전송이 가능

이럴 때 전송을 하고 수신자가 수신하기 전에는 전송자는 다음 요소를 갖고 대기한다.

만약 offer 메서드로 전송할 경우엔 버퍼의 자리가 없어 null을 반환하는데 비해 send는 대기한다

 

offer() 메서드 구조

abstract fun offer(element: E): Boolean

메서드를 보면 suspend(중단)이 업고 반환값이 Boolean인 점 

    1) 채널의 용량이 초과하지 않았다면 요소를 전송하고 true 반환

    2) 채널의 용량이 초과했다면 요소를 전송하지 못하고 false 반환

 

수신 메서드 'poll() / receive()

전송 메서드와 마찬가지로 둘의 차이는 suspend의 유무다

 

receive() 메서드 구조

abstract suspend fun receive(): E

전송의 send()와 같은 개념, 채널이 비어있으면 일시 중단(대기)

 

poll() 메서드 구조

abstract fun poll(): E?

 전송의 offer()와 같은 개념, 채널이 비어있거나 닫혀있으면 close cause Exception 반환