异步并发
async await
use std::time::Duration;
use tokio::time::sleep;
async fn hello() {
println!("Hello async Rust!");
}
async fn add(x: u32, y: u32) -> u32 {
sleep(Duration::from_millis(1000)).await;
x + y
}
#[tokio::main]
async fn main() {
hello().await;
let sum = add(1, 2).await;
println!("sum: {sum}");
}
therad
use std::thread;
use std::time::Duration;
fn main() {
// Spawning too many threads can crash this program (OS thread and memory limits)
let mut handles = vec![]; // To store thread join handles
for i in 0..1_000_000_000 { // Loop to spawn 1 million threads
handles.push(std::thread::spawn(move || { // Spawn a new OS thread
std::thread::sleep(Duration::from_millis(100)); // Simulate work (I/O wait)
println!("Thread: {} 🍔 is ready", i); // Print when done
}));
}
// Wait for all spawned threads to complete
for h in handles {
h.join().unwrap(); // Main thread waits for each spawned thread
}
}
// std::thread::spawn(move || { ... })此函数创建并启动一个新的操作系统线程。move关键字会将任何捕获的变量(例如i)的所有权转移到新线程的闭包中。它返回一个JoinHandle。
// handles向量:我们将每个元素存储JoinHandle在这个向量中。AJoinHandle允许我们等待相应的线程完成。
// std::thread::sleep(Duration::from_millis(100)):这将模拟 I/O 密集型操作,方法是暂停当前线程 100 毫秒。
// h.join().unwrap()在第二个循环中,主线程会调用join()每个线程JoinHandle。这会阻塞主线程,直到该特定线程执行完毕。unwrap()这里使用 `panic` 是为了简化操作,以便在线程发生 panic 时触发 panic。
sanyc-await 版本
use tokio::time::{sleep, Duration}; // Use tokio's sleep
// Add Tokio as a dependency in Cargo.toml:
// tokio = { version = "1", features = ["full"] }
// And use the tokio::main macro for your main function.
#[tokio::main]
async fn main() {
let mut handles = vec![]; // To store Tokio task JoinHandles
for i in 0..3_000_000_00 { // Loop to spawn 1 million async tasks
// Create an async block (a future)
let fut = async move { // move 是把所有变量都移动到fut内
sleep(Duration::from_millis(100)).await; // Asynchronous sleep
println!("Async: {} 🍔 is ready", i);
};
// Spawn the future as a Tokio task on the runtime
let handler = tokio::task::spawn(fut);
handles.push(handler);
}
// Wait for all spawned Tokio tasks to complete
for h in handles {
h.await.unwrap(); // Await the JoinHandle (which is also a future)
}
}
/* #[tokio::main]:此宏将我们的代码转换async fn main()为常规代码fn main(),该常规代码初始化 Tokio 运行时并运行async代码。
async move { ... }此语法会创建一个异步代码块。该代码块不会立即执行;相反,它会定义一个“未来”。move关键字确保所有捕获的变量(例如 `this` i)都被移到未来中。
tokio::time::sleep(Duration::from_millis(100)).await这是 Tokio 的异步睡眠模式。当.await遇到这种情况时:
该特定async代码块的执行已暂停。
控制权交还给 Tokio 执行人。
async至关重要的是,运行此代码块的操作系统线程不会被阻塞。执行器可以使用该线程来运行其他async已准备就绪的任务。
100毫秒后,Tokio 将安排此任务从中断的地方继续执行。
tokio::task::spawn(fut)此函数接收一个 future 对象fut,并将其调度到 Tokio 的线程池中运行。这是一个非阻塞操作,它会立即返回一个 future 对象JoinHandle(具体来说,是一个空对象tokio::task::JoinHandle)。该JoinHandlefuture 对象本身也是一个 future,会在生成的任务完成后解析。
h.await.unwrap()在最后一个循环中,main async函数awaits 会遍历每个任务JoinHandle。这确保main程序在退出之前等待所有一百万个“制作汉堡”任务完成。
*/
join! 所有任务必须都完成
用途: join!用于需要同时执行多个异步操作,并等待所有操作完成后再继续执行其他操作的情况。
行为:它会轮询所有提供的 future,并推动它们完成。join!只有当传递给它的所有 future 都完成后,宏本身才会完成。
返回值:完成后,join!返回一个元组。该元组包含每个 future 的结果,顺序与传递给宏的 future 顺序相同。
打个比方:你可以join!这样理解:“等待所有这些结果都返回。我需要每一个结果。”
select! 只要一个完成
用途:当您有多个异步操作,并且您只对 第select!一个完成的操作的结果感兴趣时,可以使用此方法。
行为:它会同时轮询所有提供的期货合约。一旦任何一个期货合约完成,select!它就会返回。
取消:这是一个关键区别:一旦一个 Future 任务完成并select!准备返回,所有其他正在轮询但尚未完成的 Future 任务都会立即被取消。它们的执行会被停止,并被丢弃。这可以避免不必要的工作和资源消耗。
返回值: select!返回第一个完成的单个 future 的结果。
类比运算 select!的原则是:“只要给我其中一个结果——哪个结果最早返回就给哪个。”