我正在尝试创建最简单的示例,该示例可以让 async fn hello() 最终打印出 Hello World! 。这应该在没有任何外部依赖的情况下发生,比如 tokio ,只是普通的 Rust 和 std 。如果我们可以在不使用 unsafe 的情况下完成它,则加分。

#![feature(async_await)]

async fn hello() {
    println!("Hello, World!");
}

fn main() {
    let task = hello();

    // Something beautiful happens here, and `Hello, World!` is printed on screen.
}
  • 我知道 async/await 仍然是一个夜间功能,在可预见的 future 可能会发生变化。
  • 我知道有很多 Future 实现,我知道 tokio 的存在。
  • 我只是想让自己了解标准库 future 的内部运作方式。

  • 我无助、笨拙的努力

    我模糊的理解是,首先,我需要关闭 Pin 任务。所以我继续前进
    let pinned_task = Pin::new(&mut task);
    


    the trait `std::marker::Unpin` is not implemented for `std::future::GenFuture<[static generator@src/main.rs:7:18: 9:2 {}]>`
    

    所以我想,当然,我可能需要对它进行 Box,所以我确定它不会在内存中移动。有点令人惊讶的是,我得到了同样的错误。

    到目前为止我能得到的是
    let pinned_task = unsafe {
        Pin::new_unchecked(&mut task)
    };
    

    这显然不是我应该做的。即便如此,假设我已经掌握了 Pin ned Future 。现在我需要以某种方式对其进行 poll()。为此,我需要一个 Waker

    所以我试着四处看看如何获​​得 Waker 。在 doc 上,看起来获得 Waker 的唯一方法是使用另一个接受 new_uncheckedRawWaker 。从那里我得到了 herehere ,我只是蜷缩在地板上开始哭泣。

    最佳答案

    future 堆栈的这一部分并不打算由许多人实现。我所看到的粗略估计可能会有 10 个左右的实际实现。

    也就是说,您可以通过遵循所需的函数签名来填写极其有限的执行器的基本方面:

    async fn hello() {
        println!("Hello, World!");
    }
    
    fn main() {
        drive_to_completion(hello());
    }
    
    use std::{
        future::Future,
        ptr,
        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
    };
    
    fn drive_to_completion<F>(f: F) -> F::Output
    where
        F: Future,
    {
        let waker = my_waker();
        let mut context = Context::from_waker(&waker);
    
        let mut t = Box::pin(f);
        let t = t.as_mut();
    
        loop {
            match t.poll(&mut context) {
                Poll::Ready(v) => return v,
                Poll::Pending => panic!("This executor does not support futures that are not ready"),
            }
        }
    }
    
    type WakerData = *const ();
    
    unsafe fn clone(_: WakerData) -> RawWaker {
        my_raw_waker()
    }
    unsafe fn wake(_: WakerData) {}
    unsafe fn wake_by_ref(_: WakerData) {}
    unsafe fn drop(_: WakerData) {}
    
    static MY_VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
    
    fn my_raw_waker() -> RawWaker {
        RawWaker::new(ptr::null(), &MY_VTABLE)
    }
    
    fn my_waker() -> Waker {
        unsafe { Waker::from_raw(my_raw_waker()) }
    }
    

    Future::poll 开始,我们看到我们需要一个 Pin ned future 和一个 Context Context 是从需要 Waker RawWaker 创建的。 RawWaker 需要 RawWakerVTable 。我们以最简单的方式创建所有这些部分:
  • 由于我们不尝试支持 NotReady 案例,因此我们实际上不需要为该案例做任何事情,反而可能会感到 panic 。这也意味着 wake 的实现可以是 no-ops。
  • 由于我们并不想提高效率,因此我们不需要为唤醒器存储任何数据,因此 clonedrop 基本上也可以是 no-ops。
  • 确定 future 的最简单方法是对其进行 Box,但这并不是最有效的可能性。


  • 如果你想支持 NotReady ,最简单的扩展就是有一个繁忙的循环,永远轮询。一个稍微更有效的解决方案是使用一个全局变量来指示有人调用了 wake 并阻止它变为真。

    关于asynchronous - 如何在不使用任何外部依赖项的情况下执行 async/await 函数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56252798/

    10-16 17:49