채널은 스레드가 서로 상태를 공유하는 대신 메세지를 주고받는 통신을 하도록 함으로써 동시성 코드를 작성하는 데 도움을 준다. 동시성 코드 간에 서로 안전한 통신을 할 수 있도록 해준다. 채널은 실행 중인 스레드에 상관없이 서로 다른 코루틴 간에 메세지를 안전하게 보내고 받기 위한 파이프라인으로 생각할 수 있다.
Channel의 send()는 일시 중단 함수다. 그 이유는 실제로 데이터를 수신하는 누군가가 있을 때 까지 전송하는 코드를 일시 중지하고 싶을 수도 있기 때문이다. 흔히 배압(자신이 과부하 상태라는 것을 알려 부하를 줄이는 피드백 방법)이라고 하며 리시버가 실제로 처리할 수 있는 것보다 더 많은 요소들로 채널이 넘치지 않도록 도와준다.
배압을 구성하기 위해 채널에 대한 버퍼를 정의할 수 있다. 채널을 통해 데이터를 보내는 코루틴은 채널 안의 요소가 버퍼 크기에 도달하면 일시 중단된다. 채널에서 요소가 제거되는 즉시, 송신자는 다시 재개된다.
버퍼가 없는 채널을 언버퍼드 채널이라고 한다.
언버퍼드 채널의 유일한 구현은 RendezvousChannel뿐이다. 채널 구현은 버퍼가 전혀 없어서 그 채널에서 send()를 호출하면 리시버가 receive()를 호출할 때까지 일시 중지된다. 채널 유형은 몇 가지 방법으로 인스턴스화할 수 있다.
val channel = RendezvousChannel<Int>() // 생성자를 호출해서 생성
val channel = Channel<Int>() // RendezvousChannel
// Channel() 함수를 사용할 수도 있다. 이 함수는 두가지 구현을 갖고 있는데 하나는 버퍼 용량을
// 파라미터로 받고 하나는 아무런 파라미터도 갖지 않는다. 파라미터 없이 함수를 호출해서
// RendezvousChannel을 얻을 수 있다.
val channel = Channel<Int>(0) // 버퍼 용량을 0으로 전달했을 때도 같은 결과를 얻을 수 있다.
val time = measureTimeMillis {
val channel = Channel<Int>()
val sender = GlobalScope.launch {
reapeat(10){
channel.send(it)
Log.d("Sent : ", it.toString())
}
}
channel.receive()
channel.receive()
}
실행결과 :
Sent 0
Sent 1
이 채널은 다음과 같이 요소가 검색(receive)될 때까지 송신자(send)의 실행을 중지한다.
sender 코루틴은 채널을 통해 최대 10개의 숫자까지 보낼 수 있다. 그러나 실행이 끝나기 전에
채널로부터 수신하는 요소가 두 개 뿐이어서 두 요소만 전송된다.
두 번째 유형의 채널은 버퍼를 가지는 채널이다. 이 유형의 채널은 채널 내 요소의 수가 버퍼의 크기와 같을 때마다 송신자의 실행을 중지한다. 버퍼의 크기에 따라 몇 가지 종류의 버퍼드 채널이 있다.
중단 없이 무한의 요소를 전송할 수 있는 채널을 원한다면 LinkedListChannel이 필요하다. 이 채널 유형은 어떤 송신자도 중단하지 않는다. 생성자를 사용해 Channel() 유형의 인스턴스를 얻을 수 있다.