← SEARCHABLE ARCHIVE
Rust · 2026-08-11

Rust 기초부터 마스터까지 — 9화

스레드, 채널, Mutex로 동시성 구성하기

INTERACTIVE READING MODE →

본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.

Rust는 데이터 경쟁을 만들 수 있는 공유 접근을 타입으로 제한한다. 스레드, 채널, Mutex를 사용하더라도 작업 종료와 잠금 범위는 직접 설계해야 한다.

스레드 시작과 종료

use std::thread;

let handle = thread::spawn(|| {
    load_world_data()
});

let world = handle
    .join()
    .expect("worker thread panicked");

spawn에 전달한 클로저는 새 스레드에서 실행된다. join은 작업 완료를 기다리고 panic 여부를 결과로 돌려준다.

클로저가 바깥 값을 사용한다면 스레드보다 오래 살아야 하므로 move로 소유권을 넘기는 경우가 많다.

채널로 메시지 보내기

use std::sync::mpsc;

let (sender, receiver) = mpsc::channel();

thread::spawn(move || {
    sender.send(String::from("Loaded")).unwrap();
});

if let Ok(message) = receiver.recv() {
    println!("{message}");
}

채널은 값을 공유하는 대신 한 작업에서 다른 작업으로 메시지를 이동시킨다. 송신자가 모두 사라지면 수신자는 채널이 닫혔음을 알 수 있다.

Arc와 Mutex

use std::sync::{Arc, Mutex};

let score = Arc::new(Mutex::new(0));
let worker_score = Arc::clone(&score);

let handle = thread::spawn(move || {
    let mut value = worker_score.lock().unwrap();
    *value += 10;
});

handle.join().unwrap();

Arc는 소유권을 스레드 사이에서 공유하고 Mutex는 한 번에 하나의 변경 접근만 허용한다. 잠금을 오래 잡거나 여러 잠금을 다른 순서로 얻으면 성능 저하와 교착 상태가 생길 수 있다.

동시성은 스레드 수를 늘리는 작업이 아니다. 공유 상태를 줄이고 메시지 경계를 정한 뒤, 작업 크기와 대기 시간을 실제로 측정해야 한다.