채널은 코루틴에서 지원하는 데이터 스트림 구조를 갖고 있는 일종의 자료구조이다.
val cahnnel = Channel<T>()
채널에서는 데이터를 전송 및 수신 할 수 있다.
channel.send(data)
val receiveData = channel.receive()
이렇게만 보면 Queue와 다른점을 찾기 어려워보이지만 채널의 큰 특징에는 일시정지(Suspend)가 있다.
Suspend
val channel = Channel<Int>()
CoroutineScope(Dispatchers.IO).launch {
for(i in 1..5){
channel.send(i)
println("$i send!")
}
println("send Done!")
channel.close()
}
repeat(5){
delay(2000L)
println(channel.receive())
}
원래라면 위의 코드는 5번의 채널 send가 보내진 후 2초의 딜레이마다 채널을 수신 받아 출력해야 한다.
그러나 채널은 전송 후 수신할 때까지 suspend 되기 때문에 전송 → 출력 → 전송 → 출력의 방식으로 실행된다.
그렇다면 채널은 한 개를 전송하면 수신할 때까지 기다려야 할 까?
Buffer
Buffer를 설정하면 Buffer의 개수만큼 suspend 되지 않고 전송할 수 있다.
val channel = Channel<Int>(3) // 3 << Buffer 개수
CoroutineScope(Dispatchers.IO).launch {
for(i in 1..5){
channel.send(i)
println("$i send!")
}
println("send Done!")
channel.close()
}
repeat(5){
Thread.sleep(5000L)
println("receive: ${channel.receive()}")
}
/**
1 send!
2 send!
3 send!
receive: 1
4 send!
receive: 2
5 send!
send Done!
receive: 3
...
**/
원리
채널에는 Buffer Queue와 Send Queue가 있다.
Buffer가 3인 경우
기본적으로 Send를 하면 Channel에 있는 BufferQueue에 데이터를 저장한다.
만약 buffer가 그림과 같이 다 찼는데 Send가 들어오면 어떻게 될 까?
이때 데이터는 SendQueue에 저장 된 후 Suspend를 건다.
이후 Receive로 데이터를 수집하면 Buffer가 하나 비게 되고 SendQueue에 있던 데이터가 BufferQueue로 넘어간다.
Buffer가 없는 경우
첫 번째 예시처럼 Buffer를 초기화 해주지 않으면 Buffer가 생기지 않는다.
val channel = Channel<T>()
이 경우 Buffer가 없기 때문에 Send를 하면 바로 SendQueue에 쌓고 Suspend가 걸리게 된다.
Receive를 하면 SendQueue에 있던 데이터가 수집되고, 다음 데이터는 다시 SendQueue에 쌓이는 과정이 반복된다.
Buffer를 무한으로?
val channel = Channel<T>(Channel.UNLIMITED)
다음과 같이 사용하면 총 2147483647개의 버퍼가 생긴다.
여러 스레드에서의 Channel 접근
만약 여러 스레드에서 하나의 Chaanel에 접근한다면 어떻게 될 까?
Buffer가 남아 있는 경우
만약 Buffer가 남아 있다면 Buffer에 저장이 된다.
Buffer가 꽉 차 있는 경우
일단 Channel의 suspend는 스레드 별로 동작한다.
이게 무슨 말이냐면 스레드1이 Buffer가 가득 찬 Channel에 Send를 하면
SendQueue에 데이터를 저장한 후 스레드1에 대해 일시정지를 한다.
즉, 스레드2와 3에 대해서는 아직 일시정지가 걸리지 않았기 때문에
스레드2가 Send를 한다면 해당 데이터 또한 SendQueue에 저장이 되고 스레드2도 suspend가 된다.
'프로그래밍 언어 > Kotlin' 카테고리의 다른 글
Flow Builder (0) | 2025.05.30 |
---|---|
Coroutine Flow (0) | 2025.05.30 |
[Kotlin] Scope Function (0) | 2025.05.10 |
runCatching 예외 처리 (0) | 2025.03.08 |
[Kotlin] 정렬 (0) | 2024.06.11 |