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

태스크 spawn & join

~11 min · async, spawn, join, concurrency

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

런타임은 한 번에 Future 하나 await 하는 것 이상을 하게 해. tokio::spawn, join, 그리고 join! 이 여러 async 태스크를 동시에 돌리는 도구야 — 스레드랑 채널의 async 대응물.

spawn: 동시 태스크

tokio::spawn 은 Future 를 받아 런타임에서 독립 태스크 로 돌리고, 결과를 .await 할 수 있는 핸들을 반환해. 스레드와 달리 태스크는 싸 — 런타임이 수천 개를 몇 스레드에 다중화해. spawn 이 진짜 동시성을 얻는 법이야: 여러 태스크가 각자 I/O 를 기다리면서 진척하는.

여럿을 한 번에 await

여러 Future 를 함께 기다리려면 tokio::join! 이 동시에 구동하고 다 끝날 때까지 기다려 — 하나씩 await 하는 것보다 훨씬 빨라, 대기가 겹치니까. tokio::select! 는 대신 먼저 끝나는 Future 를 기다려, 타임아웃이랑 racing 연산에 쓸모 있어.

join! 은 대기를 겹치고; 순차 .await 는 직렬화해. 1초짜리 I/O 호출 셋을 하나씩 await 하면 3초; tokio::join! 은 동시에 돌려 ~1초에 끝나. 태스크가 독립적이면 join 이 동시 async 와 그냥 순차 async 의 차이야.

태스크는 보통 Send 여야 해

tokio 가 태스크를 워커 스레드 간 옮길 수 있어서, spawn 된 Future 는 보통 Send 여야 해 — Concurrency 트랙의 그 trait. 그래서 Arc/Mutex 규칙이 여기도 적용돼: 태스크 간 상태를 Arc 로 공유하고, 컴파일러가 스레드에 하듯 async 공유를 race-free 로 유지해.

Code

독립 태스크엔 spawn, 대기 겹치려면 join!·rust
#[tokio::main]
async fn main() {
    // spawn: 독립 동시 태스크
    let h1 = tokio::spawn(async { slow_double(10).await });
    let h2 = tokio::spawn(async { slow_double(20).await });

    // 핸들을 join 해서 결과를 얻음
    let (a, b) = (h1.await.unwrap(), h2.await.unwrap());
    println!("{a} {b}"); // 20 40

    // join!: 여러 future 를 동시에 구동, 대기 겹침
    let (x, y) = tokio::join!(slow_double(1), slow_double(2));
    println!("{x} {y}"); // 2 4
}

async fn slow_double(n: i32) -> i32 { n * 2 }

External links

Exercise

각각 잠깐 tokio::time::sleep 한 뒤 숫자를 반환하는 async 함수 셋을 써. 순차적으로 (.await 하나씩) await 하고 시간을 재; 그다음 tokio::join! 으로 돌리고 그것도 재. join! 버전이 왜 제일 느린 단일 호출만큼만 빠르지?
Hint
순차 await 는 대기를 더하고; join! 은 겹쳐서 총 시간이 합이 아니라 제일 느린 단일 future 야. 그 겹침이 동시 async 의 핵심 전부야.

Progress

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

댓글 0

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

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