介绍
use futures::executor::block_on;
async fn hello_world() {
println!("hello, world!");
}
fn main() {
let future = hello_world(); // 返回一个Future, 因此不会打印任何输出
block_on(future); // 执行`Future`并等待其运行完成,此时"hello, world!"会被打印输出
}
- 使用
async
关键字定义一个异步函数,并返回一个实现了Future
特征的值,Future
理解为一个在未来某个时间点被调度执行的任务,所以直接调用async
函数不是直接返回结果。 - 使用
block_on
这个执行器(executor
)执行Future
。 block_on
会阻塞当前线程直到指定的Future
执行完成,这种阻塞当前线程以等待任务完成的方式较为简单、粗暴。好在其它运行时的执行器(executor)会提供更加复杂的行为,例如将多个future
调度到同一个线程上执行。
await
await
用于在异步函数中等待异步操作完成。它会暂停当前的异步函数,并允许其他任务运行,直到等待的操作完成。await
只能在async
函数或async
块中使用。
错误示例
async fn hello_world() {
hello_cat();
println!("hello, world!");
}
async fn hello_cat() {
println!("hello, kitty!");
}
fn main() {
let future = hello_world();
block_on(future); // 只输出了 "hello, world!"
}
正确示例
async fn hello_world() {
hello_cat().await;
println!("hello, world!");
}
async fn hello_cat() {
println!("hello, kitty!");
}
fn main() {
let future = hello_world();
block_on(future);
}
// 输出:
// hello, kitty!
// hello, world!
证明 FeatureB 是在 FeatureA 运行后才被继续执行的
use tokio::time::{sleep, Duration};
async fn hello_world() { // FeatureB
println!("task 1 begin: {:?}-{}", thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
hello_cat().await;
println!("task 1 end: {:?}-{}", thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
println!("hello, world!");
}
async fn hello_cat() { // FeatureA
sleep(Duration::from_secs(2)).await; // 模拟耗时
println!("hello, kitty!");
println!("task2 end: {:?}", thread::current().id())
}
#[tokio::main]
async fn main() {
println!("main begin: {:?}", thread::current().id());
let future = hello_world();
block_on(future); // 只输出了 "hello, world!"
println!("main end: {:?}", thread::current().id());
}
// 输出:
// main begin: ThreadId(1)
// task 1 begin: ThreadId(1)-2024-10-07 22:56:06
// hello, kitty!
// task2 end: ThreadId(1)
// task 1 end: ThreadId(1)-2024-10-07 22:56:08
// hello, world!
// main end: ThreadId(1)
在异步编程中,休眠操作不是阻塞当前线程,而是将控制权让出给其他任务,这样可以实现高效的并发处理,但是在FeatureA执行期间被休眠时,FeatureB并没有继续运行,因为两个任务都运行在同一个线程中
处理多个Future
并行处理
async fn task1() -> &'static str {
println!("task 1 begin: {:?}-{}", thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
sleep(Duration::from_secs(1)).await;
println!("task 1 end: {:?}-{}", thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
"Task 1 completed"
}
async fn task2() -> &'static str {
println!("task 1 begin: {:?}-{}", thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
sleep(Duration::from_secs(2)).await;
println!("task 1 end: {:?}-{}", thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
"Task 2 completed"
}
#[tokio::main]
async fn main() {
let (res1, res2) = tokio::join!(task1(), task2());
println!("{}, {}", res1, res2); // Task 1 completed, Task 2 completed
}
// 输出:
// task 1 begin: ThreadId(1)-2024-10-07 23:00:58
// task 1 begin: ThreadId(1)-2024-10-07 23:00:58
// task 1 end: ThreadId(1)-2024-10-07 23:00:59
// task 1 end: ThreadId(1)-2024-10-07 23:01:00
// Task 1 completed, Task 2 completed
顺序执行
#[tokio::main]
async fn main() {
let res1 = task1().await;
println!("{}", res1);
let res2 = task2().await;
println!("{}", res2);
}
// 输出:
// task 1 begin: ThreadId(1)-2024-10-07 23:02:11
// task 1 end: ThreadId(1)-2024-10-07 23:02:12
// Task 1 completed
// task 1 begin: ThreadId(1)-2024-10-07 23:02:12
// task 1 end: ThreadId(1)-2024-10-07 23:02:14
// Task 2 completed
竞态任务
#[tokio::main]
async fn main() {
let result = tokio::select! {
res = task1() => res,
res = task2() => res,
};
println!("First completed task: {}", result);
}
// 输出:
// task 1 begin: ThreadId(1)-2024-10-07 23:03:45
// task 1 begin: ThreadId(1)-2024-10-07 23:03:45
// task 1 end: ThreadId(1)-2024-10-07 23:03:46
// First completed task: Task 1 completed
- 可能需要处理竞态任务,即同时启动多个 Future,并在第一个完成时返回结果
tokio::select!
将等待两个任务中的第一个完成,并输出结果
并行处理Feature集合
use rand::Rng;
async fn task(n: u64) -> u64 {
println!("task {} begin: {:?}-{}", n, thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
sleep(Duration::from_secs(rand::thread_rng().gen_range(1..=5))).await;
println!("task {} end: {:?}-{}", n, thread::current().id(), Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
n + 1
}
#[tokio::main]
async fn main() {
let futures: Vec<_> = (1..=5).map(task).collect();
let results = join_all(futures).await;
println!("Results: {:?}", results);
}
// 输出:
// task 1 begin: ThreadId(1)-2024-10-07 23:14:29
// // task 2 begin: ThreadId(1)-2024-10-07 23:14:29
// // task 3 begin: ThreadId(1)-2024-10-07 23:14:29
// // task 4 begin: ThreadId(1)-2024-10-07 23:14:29
// // task 5 begin: ThreadId(1)-2024-10-07 23:14:29
// // task 5 end: ThreadId(1)-2024-10-07 23:14:31
// // task 4 end: ThreadId(1)-2024-10-07 23:14:32
// // task 1 end: ThreadId(1)-2024-10-07 23:14:34
// // task 2 end: ThreadId(1)-2024-10-07 23:14:34
// // task 3 end: ThreadId(1)-2024-10-07 23:14:34
// // Results: [2, 3, 4, 5, 6]
- Future将输入值加1
join_all
会并行执行 Future 集合中的所有任务,并返回一个包含每个 Future 结果的 Vec- 执行过程并非是串行的,在当前执行的 Future 休眠的过程中,当前线程并不会阻塞,而是继续执行其他线程
串行Stream处理Feature集合
use tokio::time::{sleep, Duration};
use tokio_stream::{StreamExt, iter};
use rand::Rng;
#[tokio::main]
async fn main() {
let tasks = (1..=5).map(task);
let mut stream = iter(tasks);
while let Some(result) = stream.next().await {
println!("Task completed with result: {}", result.await.to_string());
}
}
// 输出:
// task 1 begin: ThreadId(1)-2024-10-07 23:22:33
// task 1 end: ThreadId(1)-2024-10-07 23:22:35
// Task completed with result: 2
// task 2 begin: ThreadId(1)-2024-10-07 23:22:35
// task 2 end: ThreadId(1)-2024-10-07 23:22:37
// Task completed with result: 3
// task 3 begin: ThreadId(1)-2024-10-07 23:22:37
// task 3 end: ThreadId(1)-2024-10-07 23:22:38
// Task completed with result: 4
// task 4 begin: ThreadId(1)-2024-10-07 23:22:38
// task 4 end: ThreadId(1)-2024-10-07 23:22:40
// Task completed with result: 5
// task 5 begin: ThreadId(1)-2024-10-07 23:22:40
// task 5 end: ThreadId(1)-2024-10-07 23:22:41
// Task completed with result: 6
- 使用
tokio-stream
提供的 Stream 处理功能来持续处理多个 Future 生成的结果 - 异步流中的任务会一个接一个地执行,并处理每个任务的结果