为什么异步? 这取决于我们要写什么,如果我们的示例函数正在等待从服务器下载一个 10mb 的文件
如果我们从 main 函数中调用 get_file() 函数,它将阻塞整个主线程
fn get_file() -> Vec {
// request to server and recv 10mb of raw bytes
}fn extra_bytes() -> Vec {
// request to server and recv 40mb of raw bytes
}fn main() {
let bytes = get_file(); // this is gonna take a while
let extra_bytes = get_extra(); // this is also gonna take a while
let convert_string = String::from_utf8(&bytes).unwarp(); // gonna be a while before we reach this line
} 假设 get_file() 需要 0.3 秒,get_extra() 需要 0.5 秒,主函数需要 0.8 秒才能继续执行。
由于 get_file() 和 get_extra() 不依赖于彼此的完成,我们可以在线程中生成它们(旧线程方法)
但我们不会有任何关于线程当前状态或进度的信息
Futures
Future Trait 就像操作结果的占位符
trait Future {
type Ouput;
fn poll(self: Pin<&mut Self>, cx: &mut Context<’_>) -> Poll;
}
异步编程通过实现 Future 特征为我们提供了对任务的抽象,它允许任务在被阻塞时让步,并在准备好继续或完成时将自身重新添加到执行中。
当 Future 认为它现在已经准备好时,我们还需要有人检查 Future 的更新。
执行器帮助进行更新检查。
因此,如果我们创建两个期货 get_file() 和 extra_bytes(),我们的执行器将有两个期货,并且执行器将在这些期货上调用 poll
如果 Future 没有设置(唤醒)何时启动它,执行者将不知道启动它是否会取得任何进展或是否完成
Future Trait 将定义作为实现此 trait 的操作的结果返回的具体类型
poll 方法定义了当前状态将如何被发回,它返回一个 Enum Poll
enum Poll {
Ready(T),
Pending
}Ready(Output) -> if the result of the operation is ready
Pending -> if the operation is still going on Executor 只是一个队列,Futures 被添加到队列中,executor 将调用这些 futures 的 poll 方法
假设我们将两个作业添加到 Executor 队列
为了开始工作,Executor 将在队列中的所有 Futures 中调用 Poll 方法
每个 Future 都会有自己的实现,当它希望自己再次添加到执行器队列中时。
在 Rust 术语中,
Executor 将是一个 Receiver Stream 并且用户将 Futures 添加到流中以使用发送者开始操作,并且 Futures 本身具有发送者的克隆。
例子:
TimerFuture 是需要运行的作业
它实现了 Future Trait,每次 executor 调用这个 poll 方法时,它会检查 shared_state 是否已完成,或者它会克隆 executor 传递的 Waker 以在它准备好时再次将自己推送到流中
Future 中的工作非常简单,它在传递的持续时间内休眠,并在最初为此 Future 调用轮询时克隆的唤醒器中调用唤醒方法
wake 方法会再次将此 Future 添加到 executor 流中,这一次当 executor 调用 poll 时,作业将以就绪状态完成。
因此,通过一个线程,我们能够获得许多期货异步运行的错觉
这只是关于如何在 Rust 中实现异步系统的简短介绍,我将返回一个允许我异步进行多次下载的工作示例。 如果您对示例项目有其他想法,请告诉我。
关注七爪网,获取更多APP/小程序/网站源码资源!
| 留言与评论(共有 0 条评论) “” |