rust bounded vs unbounded

wangki·2025년 9월 14일

Rust

목록 보기
52/56

개요

rust의 tokio는 여러 비동기 채널을 제공해준다.
https://docs.rs/tokio/1.47.1/tokio/sync/index.html

mpsc 채널에서 boundedunbounded 채널의 차이점에 대해서 알아볼 예정이다.

unbounded의 경우 무한 버퍼를 가지고 있고, 송신 측에서 대기없이 무조건 데이터를 보내는 것으로 알고 있다. 채널을 통해 구현할 필요가 있을 때, 단순히 버퍼를 얼마큼 정의해 줘야 할 지 잘 몰라서 unbounded채널로 만든 적이 많다. 그러면 unbounded만 쓰면 되는데 왜 bounded도 존재하는지 의문이 들어서 조사하여 boundedunbounded의 차이점에 대해서 설명해 보겠다.

내용

1. unbounded Channel (무제한 버퍼 채널)

unbounded채널은 이름 그대로 버퍼의 크기가 무한인 채널이다. 송신자는 수신자의 처리 속도와 관계없이 데이터를 버퍼에 즉시 보낼 수 있다. 이 과정에서 대기를 하지 않는다.

장점

  • 버퍼의 크기를 고민할 필요가 없다.
  • 송신자는 블로킹 없이 빠르게 데이터를 계속 보낼 수 있어, 송신 작업이 매우 빨라야할 때 유리하다.

단점

  • 송신자가 수신자보다 훨씬 빠르게 데이터를 보내면, 미처리된 데이터가 메모리에 계속 쌓이게 된다. 이는 메모리 부족 현상을 일으켜 프로그램이 비정상 종료될 수 있다.
  • 송신 속도를 제어할 수 있는 메커니즘이 없어, 예상치 못한 부하가 발생할 수 있다.

2. Bounded Channel (유한 버퍼 채널)

bounded 채널은 버퍼의 크기가 고정된 채널입니다. 채널을 생성할 때 buffer_size를 지정해야 합니다.

장점

  • 버프 크기가 제한되어 있어 메모리 사용량이 예상 가능하고, 메모리 고갈 위험이 없다.
  • 송신자의 속도를 자동으로 낮추는 백프레셔 메커니즘을 제공하여 시스템 전체의 안정성을 높인다.

단점

  • 버퍼가 가득 차면 송신자가 대기하기 때문에, 송신 속도가 수신자의 처리 속도에 영향을 받는다.
  • 적절한 버퍼 크기를 결정해야 하는 고민이 필요하다.

위 내용은 unboundedbounded의 특징과 장단점에 대해서 간략히 정리한 내용이다. 그래서 어떤 채널을 언제 사용해야 하는지 궁금하다.

먼저 bounded에서 송신자의 속도가 수신자의 처리 속도에 영향을 받는다는 것을 테스트 코드로 작성해 보았다.

use std::time::Duration;



#[tokio::main]
async fn main() {
    let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(5);

    tokio::spawn(async move {
        loop {
            let cloned_tx = tx.clone();    
            let send_msg = "hi".to_string();
            cloned_tx.send(send_msg).await.unwrap();
            println!("data 보냈음!");
            tokio::time::sleep(Duration::from_millis(100)).await;
        }
    });
    
    
    loop {
        // heavy working.....
        tokio::time::sleep(Duration::from_secs(10)).await;
        match rx.recv().await {
            Some(data) => {
                // data를 처리하기 전, 5초 대기 
                println!("처리 완료!: {}", data);
            },
            None => todo!(),
        }
    }
    
}

버퍼 크기를 5로 설정했으므로, 5번의 데이터를 보낸 후 대기하는 것을 볼 수 있다. 수신 측에서 heavy working을 10초로 대기하는 것으로 설정하여 수신 처리를 늦게 하였더니, 송신 측에서 대기하는 것을 볼 수 있었다. 시스템의 안정성이 중요하거나, 수신 측에 처리에 따라서 송신 측을 컨트롤하여 과부하를 방지할 수 있는 시스템에서 사용하기 적절한 것 같다. 해당 스펙에 맞게 부하 테스트를 하면서 적합한 버퍼 크기를 결정하는 것도 하나의 방법일 것 같다.

그리고 unbounded의 경우에는 절대로 실패해서는 안 되는 중요한 메시지를 보낼 때 고려해볼 수 있을 것 같다. 수신측에서 부하가 걸리더라도 송신자가 즉시 메시지를 처리할 수 있기 때문이다. 그리고 송신의 부하가 예측이 가능한 경우도 선택할 수 있을 것 같다. unbounded채널을 사용할 경우에는 수신측에서 무거운 작업을 처리하도록 하는 것은 피해야할 것 같다.

결론

둘 사이의 차이와 언제 사용하면 좋은지 알아보았다. 위에서 알아본 내용으로 몸소 경험한 바가 없어서 정확히 이해는 못 했지만, 앞으로 채널을 사용할 때, 각 채널의 장단점을 고려하고 어떤 채널을 사용하는 것이 더욱 좋은 선택일지 생각하고 사용해 봐야겠다.

0개의 댓글