C.W.K.
Stream
Lesson 05 of 05 · published

Async vs 스레드 — 고르기

~11 min · async, threads, decision, spawn-blocking

Level 0Rust 호기심러
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

제일 흔한 async 실수는 스레드가 더 단순할 때 async 로 손 뻗는 것 — 또는 반대. 이 레슨은 결정 프레임워크야: 언제 async, 언제 스레드, 어떻게 구분하나.

핵심 구분

I/O 바운드 일 (네트워크, 디스크, 타이머 대기) 은 async 에 맞아: 태스크가 시간 대부분을 기다려서, 한 스레드가 각 await 에서 전환해 수천 개를 다뤄. CPU 바운드 일 (수 계산, 파싱, 압축) 은 스레드 에 맞아: 양보할 대기가 없으니, 코어 간 진짜 병렬을 원해.

함정: async 안 블로킹

async 태스크 안의 CPU 무거운 또는 블로킹 연산은 footgun 이야: 양보를 안 해서, 그 스레드의 다른 모든 태스크를 굶겨. async 맥락에서 블로킹 일을 꼭 해야 하면 tokio::task::spawn_blocking 한테 넘겨, 전용 스레드 풀에서 돌려 async 태스크가 계속 흐르게. 두 모델을 부주의하게 섞는 게 1위 async 성능 버그야.

모델을 워크로드에 맞추고, async 실행기를 블록하지 마. I/O 바운드 고동시성 -> async. CPU 바운드 병렬 -> 스레드. async 안 블로킹 일 -> spawn_blocking. 잘못된 짝은 크래시 안 해 — 처리량을 조용히 파괴해, 크래시보다 디버깅 어려워.

둘 다 쓸 수 있어

진짜 시스템은 섞어: CPU 무거운 이미지 리사이즈를 spawn_blocking 이나 스레드 풀로 떼어내는 async 웹 서버 (tokio). 모델은 라이벌이 아니야 — 다른 모양의 일을 위한 도구고, 성숙한 Rust 서비스는 각각 맞는 데 써. 어느 게 어느 건지 아는 게 문법만이 아니라 동시성을 진짜 이해하는 사람의 표식이야.

Code

I/O 엔 async, CPU 일엔 spawn_blocking·rust
#[tokio::main]
async fn main() {
    // I/O 바운드: async 가 빛남 (많은 동시 대기)
    let io = tokio::spawn(async { fetch().await });

    // CPU 바운드: 실행기를 안 굶기게 블로킹 스레드로 떼어냄
    let cpu = tokio::task::spawn_blocking(|| heavy_compute());

    println!("{} {}", io.await.unwrap(), cpu.await.unwrap());
}

async fn fetch() -> i32 { 1 }   // 가정: 네트워크를 await
fn heavy_compute() -> i32 {      // 순수 CPU: 스레드에 속함
    (1..=1000).sum()
}

External links

Exercise

async 서버용으로 이것들을 분류하고 맞는 도구를 정해: HTTP 요청 서빙, bcrypt 로 비밀번호 해싱 (CPU 무거움), 시작 시 디스크에서 설정 한 번 읽기. 각각: 평범한 async, spawn_blocking, 또는 보통 스레드 — 그리고 왜인지 말해.
Hint
HTTP 서빙은 I/O 바운드 -> 평범한 async. bcrypt 는 CPU 무겁고 실행기를 멈출 것 -> spawn_blocking. 일회성 시작 읽기는 런타임 시작 전 평범한 sync 코드거나 단일 async 읽기 — 어느 쪽이든 hot path 가 아니야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.