Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

동시성: 타입 시스템이 보장하는 스레드 안전성

학습 목표: Rust가 어떻게 컴파일 타임에 데이터 경합(Data Race)을 원천 차단하는지 배웁니다. C#의 관례적인 lock 방식과 Rust의 Arc<Mutex<T>> 방식을 비교하고, **Send**와 Sync 트레이트를 통해 "공포 없는 동시성(Fearless Concurrency)"을 실천하는 법을 익힙니다.


1. C# vs Rust: 스레드 안전의 철학 차이

C#에서 스레드 안전성은 개발자의 기억력에 의존합니다. lock을 잊어버리면 런타임에 데이터가 꼬이지만, Rust는 데이터를 공유하려면 반드시 안전한 구조(Arc, Mutex 등)를 갖춰야만 컴파일이 가능합니다.

비교 항목C# (Lock-based)Rust (Ownership-based)
안전성 보장런타임/개발자 책임컴파일 타임 강제
데이터 경합발생 가능 (디버깅 지옥)원천 차단 (컴파일 에러)
공유 방식수동 lock(obj) { ... }Arc<Mutex<T>> / 메시지 패싱
성능 오버헤드가상 디바이스, 컨텍스트 스위칭제로 비용 추상화 (Atomic 활용)

2. 핵심 도구: Arc<Mutex<T>>

여러 스레드가 하나의 데이터를 안전하게 읽고 쓰기 위한 가장 표준적인 조합입니다.

  • Arc<T>: 원자적 참조 횟수 계산기. 여러 주인(스레드)이 데이터를 가질 수 있게 합니다.
  • Mutex<T>: 데이터를 '상호 배제' 락으로 감쌉니다. 락을 얻어야만 데이터에 접근할 수 있습니다.
#![allow(unused)]
fn main() {
let counter = Arc::new(Mutex::new(0));

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1; // 락을 얻은 상태에서만 수정 가능
    });
}
}

3. 메시지 패싱 (Channels)

"메모리를 공유해서 통신하지 말고, 통신해서 메모리를 공유하라"는 철학입니다. 채널을 통해 데이터를 주고받으면 락 없이도 안전하게 협업할 수 있습니다.

#![allow(unused)]
fn main() {
let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    tx.send("데이터 전송").unwrap();
});

let msg = rx.recv().unwrap();
}

4. SendSync: 스레드 안전의 증표

Rust 컴파일러가 타입을 검사할 때 사용하는 마커 트레이트입니다.

  • Send: 소유권을 다른 스레드로 넘겨도 안전한 타입 (이동 가능).
  • Sync: 여러 스레드에서 참조(&T)를 공유해도 안전한 타입.

💡 실무 팁: rayon으로 병렬화 뚝딱!

복잡한 스레드 관리 없이 리스트 처리를 병렬로 하고 싶다면 rayon 크레이트를 쓰세요. .iter().par_iter()로 바꾸기만 해도 모든 CPU 코어를 활용해 데이터를 처리합니다. 빌림 검사기가 안전을 보장하므로 안심하고 지를 수 있습니다.