태스크

Rust의 태스크(작업) 시스템은 경량 스레딩의 한 종류로 볼 수 있습니다.

하나의 작업에는, 실행자가 이 작업을 진행하기 위해 계속 폴링하는, 최상위 future가 한 개 있습니다. 이 future에는 poll 메서드가 폴링하는 중첩된 future가 한 개 이상 있을 수 있습니다. 이러한 중첩된 future는 일반적인 함수 호출 스택하고 비슷한 역할을 합니다. 한 작업 안에서 여러 자식 future들을 폴링하면, 타이머를 켜는 것과 어떤 I/O작업을 동시에 수행시킨 후 타이머와 I/O 중 먼저 끝나는 것을 기다리는 것과 같은동시성도 구현할 수 있습니다.

use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() -> io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:0").await?;
    println!("포트 {}에서 수신 대기", listener.local_addr()?.port());

    loop {
        let (mut socket, addr) = listener.accept().await?;

        println!("{addr:?}에서 연결");

        tokio::spawn(async move {
            socket.write_all(b"누구세요?\n").await.expect("소켓 오류");

            let mut buf = vec![0; 1024];
            let name_size = socket.read(&mut buf).await.expect("소켓 오류");
            let name = std::str::from_utf8(&buf[..name_size]).unwrap().trim();
            let reply = format!("{name}님, 전화해 주셔서 감사합니다.\n");
            socket.write_all(reply.as_bytes()).await.expect("소켓 오류");
        });
    }
}

이 예제를, 로컬 컴퓨터에 만들어 둔 src/main.rs에 복사하고 거기에서 실행하세요.

nc 또는 telnet과 같은 TCP 연결 도구를 사용하여 연결해 보세요.

  • 수강생들에게 이 서버에 몇 개의 클라이언트가 연결되면 이 서버의 상태가 어떻게 변할지 그림을 그려보도록 하세요. 어떤 태스크들이 있는지, 이 태스크들의 Future는 어떤 상태에 있는지 물어봅니다.

  • This is the first time we've seen an async block. This is similar to a closure, but does not take any arguments. Its return value is a Future, similar to an async fn.

  • Async 블록을 함수로 리팩터링하고 ?를 사용하여 오류 처리를 개선해 봅시다.