引言
在 Rust 异步编程中,Task(任务)是一个非常重要的概念。本文将带你深入理解 Rust 中的异步任务系统,并通过代码示例一步步实现一个简单的任务调度器。
什么是 Task?
Task 是最简单的顶层 Future,由主循环拥有并轮询。它类似于线程,但比线程更轻量级。我们可以使用 tokio::task::spawn
来创建新的任务:
#[tokio::main]
async fn main() {
let mut task_handles = Vec::new();
// 创建 10 个任务
for n in 1..=10 {
task_handles.push(tokio::task::spawn(foo(n)));
}
// 等待所有任务完成
for handle in task_handles {
handle.await.unwrap();
}
}
实现自己的任务系统
让我们一步步实现一个简单的任务系统:
首先,定义任务类型:
// 定义动态 Future 类型
type DynFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
// 存储新任务的全局变量
static NEW_TASKS: Mutex<Vec<DynFuture>> = Mutex::new(Vec::new());
实现 spawn 函数:
// 用于创建新任务的函数
fn spawn<F: Future<Output = ()> + Send + 'static>(future: F) {
NEW_TASKS.lock().unwrap().push(Box::pin(future));
}
实现主循环:
fn main() {
let mut tasks: Vec<DynFuture> = Vec::new();
let waker = futures::task::noop_waker();
let mut context = Context::from_waker(&waker);
loop {
// 轮询现有任务,移除已完成的任务
tasks.retain_mut(|task| {
task.as_mut().poll(&mut context).is_pending()
});
// 收集并处理新任务
loop {
let Some(mut task) = NEW_TASKS.lock().unwrap().pop() else {
break;
};
if task.as_mut().poll(&mut context).is_pending() {
tasks.push(task);
}
}
// 如果没有任务,退出循环
if tasks.is_empty() {
break;
}
// 处理唤醒时间并休眠...
}
}
关键概念解析
Send 和 Sync:任务需要实现 Send
trait 以保证线程安全'static 生命周期:确保任务不持有可能失效的引用 动态分发:使用 dyn Future
允许处理不同类型的 FuturePin:保证 Future 在内存中的位置不会改变
总结
通过本文,我们学习了:
Task 的基本概念和作用 如何实现一个简单的任务调度系统 Rust 异步编程中的重要概念 线程安全和生命周期的处理方法
这些知识对于理解 Rust 异步编程至关重要,也是构建高性能异步应用的基础。
参考文章
Async Rust Part Two: Tasks:https://jacko.io/async_tasks.html Rust 官方文档:https://doc.rust-lang.org/book/
书籍推荐
各位 Rust 爱好者,今天为大家介绍一本《Programming Rust: Fast, Safe Systems Development》(第二版) 是由 Jim Blandy、Jason Orendorff 和 Leonora Tindall 合著的 Rust 编程指南。本书深入探讨了 Rust 语言在系统编程中的应用,着重介绍如何利用 Rust 的独特特性来平衡性能和安全性。书中涵盖了 Rust 的基础数据类型、所有权和借用概念、特征和泛型、并发编程、闭包、迭代器以及异步编程等核心内容。这本更新版基于 Rust 2021 版本,为系统程序员提供了全面而实用的 Rust 编程指导。