스레드는 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 연산이 많을 때 특정해 손 뻗어.