Skip to content

Async

  • 大致概念:[80%-sure] 以 Rust 为例,运行到 func().await 时 (func 为 async fn)

    • func() 内部代码完全交由 async runtime 处理
    • func() 内部通常有等待IO/GPU/定时器的异步部分。这些部分不应该让 CPU 干等着,所以才写成异步函数.
    • 如果 func() 内部全是同步代码,没有 .await 或手动挂起点,写成异步(async fn)没有实际意义
      • 它是不会挂起的,会同步执行完毕。
  • 轮询

    • poll 调用次数不是固定的,取决于任务的完成时机和外部事件。

Rust

async fn my_async(x: u32) -> u32 {
    let a = x + 1;
    a * 2
}

let fut = my_async(10);
fut.await; // 开始轮询
  • 编译器生成的大致逻辑:
enum MyAsyncState {
    Start { x: u32 },
    Done,
}

struct MyAsyncFuture {
    state: MyAsyncState,
}

impl Future for MyAsyncFuture {
    type Output = u32;

    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<u32> {
        match self.state {
            MyAsyncState::Start { x } => {
                let a = x + 1;
                let result = a * 2;
                self.state = MyAsyncState::Done;
                Poll::Ready(result)
            },
            MyAsyncState::Done => {
                panic!("polled after completion");
            }
        }
    }
}
  • 另一个例子:
async fn my_async(x: u32) -> u32 {
    let a = x + 1;
    tokio::time::sleep(Duration::from_secs(3)).await;
    a * 2
}
enum MyAsyncState {
    Start { x: u32 },
    Waiting { a: u32, sleep_future: Sleep },
    Done,
}

impl Future for MyAsyncFuture {
    type Output = u32;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u32> {
        loop {
            match self.state {
                MyAsyncState::Start { x } => {
                    let a = x + 1;
                    let sleep_future = tokio::time::sleep(Duration::from_secs(3));
                    self.state = MyAsyncState::Waiting { a, sleep_future };
                },
                MyAsyncState::Waiting { a, ref mut sleep_future } => {
                    match Pin::new(sleep_future).poll(cx) {
                        Poll::Pending => return Poll::Pending,
                        Poll::Ready(_) => {
                            let result = a * 2;
                            self.state = MyAsyncState::Done;
                            return Poll::Ready(result);
                        }
                    }
                },
                MyAsyncState::Done => {
                    panic!("polled after completion");
                }
            }
        }
    }
}