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

Async 는 왜 존재하나

~11 min · async, io-bound, concurrency

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

스레드는 CPU 바운드 병렬에 좋지만, 만 단위 동시 I/O 연산엔 확장이 안 돼 — OS 스레드마다 메모리랑 컨텍스트 스위칭 비용이 들거든. async/await 가 고동시성 I/O 에 대한 Rust 의 답이야: 한 줌의 스레드 위에 수천 작업.

async 가 푸는 문제

10,000 연결을 다루는 웹 서버는 대부분 기다려 — 네트워크, 데이터베이스, 디스크를. 놀고 있는 연결마다 OS 스레드를 주면 메모리를 낭비해. async 는 한 스레드가 수천 작업을 다루게 해: 한 작업이 I/O 를 기다리는 동안 스레드가 다른 걸 돌려. 작업마다 스레드 없는 동시성이야.

선점이 아니라 협력

async 작업은 협력적 이야: 작업이 뭔가 기다리는 .await 지점에 닿을 때까지 돌고, 자발적으로 스레드를 양보해 다른 작업이 돌게. await 사이에 OS 선점이 없어. 그게 async 가 I/O 바운드 일 (많은 대기) 에 뛰어나고 CPU 바운드 일 (자연스러운 양보 지점 없음) 엔 틀린 도구인 이유야.

CPU 바운드 병렬엔 스레드; I/O 바운드 동시성엔 async. 작업이 시간 대부분을 기다리는 데 (네트워크, 디스크, 타이머) 쓰면 async 가 큰 숫자로 싸게 확장해. 시간 대부분을 계산하는 데 쓰면 스레드 (또는 스레드 풀) 가 맞는 도구야. 모델을 워크로드에 맞추는 게 결정 전부야.

async 는 opt-in 이야

모든 게 async 인 일부 언어와 달리, Rust 는 그걸 opt-in 이고 zero-cost 로 유지해: async 를 쓰는 곳에서만 비용을 내고, 동기 코드는 단순하게 남아. 대부분 프로그램은 async 가 아예 필요 없어. 관리할 동시 I/O 연산이 많을 때 특정해 손 뻗어.

Code

블로킹 vs async: 누가, 어떻게 기다리나·rust
// 동기: 이 함수는 '기다리는' 동안 스레드를 블록해
fn fetch_blocking() -> String {
    // 스레드 전체를 묶는 느린 네트워크 호출을 상상해
    String::from("data")
}

// 비동기: Future 를 반환; await 하면 기다리는 동안 스레드를 양보
async fn fetch_async() -> String {
    // 여기 진짜 .await 면 대기 동안 다른 작업이 돌게 해
    String::from("data")
}

fn main() {
    let _f = fetch_async(); // 이건 Future — 아직 실행 안 됨
    println!("{}", fetch_blocking());
}

External links

Exercise

진짜 작업 셋을 적고 각각 I/O 바운드인지 CPU 바운드인지 라벨 붙여: 웹페이지 1000개 다운로드, 이미지 1000장 리사이즈, 데이터베이스 1000번 쿼리. 각각 async 와 스레드 중 뭐가 더 맞는지 정하고 한 문장으로 왜인지 말해.
Hint
다운로드랑 쿼리는 I/O 바운드 (대부분 대기) -> async 가 싸게 확장. 이미지 리사이즈는 CPU 바운드 (대부분 계산) -> 스레드가 코어 간 진짜 병렬을 줘.

Progress

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

댓글 0

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

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