asynchronous - 如何在不使用任何外部依赖项的情况下执行 async/await 函数?
问题描述
我正在尝试创建可以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
实现,我知道tokio
. - 我只是想让自己了解标准库期货的内部运作。
我无助而笨拙的努力
我的模糊理解是,首先,我需要完成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
. 在文档上,似乎获得 a 的唯一方法Waker
是使用另一个new_unchecked
接受 a 的方法RawWaker
。从那里我到这里又从那里,我只是蜷缩在地板上开始哭泣。
解决方案
期货堆栈的这一部分并不打算由许多人来实现。我所看到的粗略估计可能会有 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
需要 a 的 a创建的RawWaker
。ARawWaker
需要一个RawWakerVTable
. 我们以最简单的方式创建所有这些部分:
由于我们不尝试支持
NotReady
案例,因此我们不需要为该案例实际做任何事情,而是可以恐慌。这也意味着 的实现wake
可以是无操作的。由于我们没有试图提高效率,我们不需要为我们的唤醒器存储任何数据,因此基本上
clone
也drop
可以是无操作的。确定未来的最简单方法就是确定未来
Box
,但这不是最有效的可能性。
如果你想支持NotReady
,最简单的扩展是有一个繁忙的循环,永远轮询。一个稍微更有效的解决方案是有一个全局变量来指示有人调用wake
并阻止它成为真的。
推荐阅读
- javascript - 如何将函数从类父组件传递给子功能组件
- html - 为什么我的电子邮件签名文本颜色未在浏览器中显示?
- powershell - 在 Conda 环境中创建的文件和文件夹不会出现在我的计算机上
- node.js - MongoDB对象数组中的自动完成搜索
- python - 使用 Python Paramiko SSH 服务器实现 SCP 服务器
- html - 角度一次选择多个按钮
- python - Python pywhatkit 表情符号
- julia - Julia 函数根据调用者函数选择结构成员
- angular - 如何测试 Angular Router Resolve 方法?
- swift - SwiftUI 多个警报在一个视图中