概述
*异步编程参考书籍: async-book
此学习根据Rust语言圣经 中tokio专栏,tokio 是一个将 rust提供的async/await 特性编写的异步代码运行起来的异步运行时. (tokio,async-std, smol等异步运行时框架)
- 为什么要使用 异步运行时Tokio
rust本身只提供异步编程时候所需要的基本特性,标准库中Future特性,这些特性单独使用没有多大用处,需要有一个运行时将这些特性实现代码运行起来 ,选择使用Tokio 基于Future,和MONO 两种系统的框架运行的
async/await 介绍
async作为rust异步编程的工具,async 将一个代码块转换成实现Future特征的状态机, 在转换成为Future后作用:在同步方法中调用阻塞函数(asyn转换函数)会阻塞整个线程,但是阻塞的Future会让出线程控制权,让其他Future 运行。
- 状态机器四要素: 状态,动作,事件,跳转。
动作:打开 关不, 事件: 按按钮
在Tokio中 异步运行时候提供核心 一个reactor(订阅机制), 多个executor执行器
use futures::executor;
// 异步关键字
async fn hello(){
println!("Hello!");
}
fn main() {
// 执行器组塞式调用
executor::block_on(hello());
println!("Hello, world!");
}
-- 通过await方式调用 不阻塞方式调用
use futures::{self, executor};
use std::thread::sleep;
use std::time::Duration;
async fn learn_song() {
sleep(Duration::from_secs(5));
println!("learn song");
}
async fn sing_song() {
println!("sing song");
}
async fn dance() {
println!("dance");
}
// 先学会唱歌,唱歌 后在跳舞
async fn learn_and_sing_song() {
learn_song().await;
sing_song().await;
}
async fn async_main(){
let f1 = learn_and_sing_song();
let f2 = dance();
futures::join!(f1, f2); // 使用 join 方法进行了阻塞
}
fn main() {
executor::block_on(async_main());
println!("Hello, world!");
}
sing song
dance
Hello, world!
经典:问题一:
本来根据async 的定义 当某一个Future线程被阻塞(睡眠)后应该让出线程控制权,让其他的线程运行 ,但rust代码中执行结果不对, 还是按照阻塞方式顺序执行 这是为什么???
executor::join!() 保证两个异步函数并行执行
实际执行为什么会不符合预期结果
主要原因在于 Sleep 函数, 在rust中使用Sleep函数后 认为你是将本线程进行休眠,不是进行阻塞,如果想要达到阻塞的情形, 使用 async-std, tokio 等运行时框架的 delay_for
use futures;
use tokio::runtime::Runtime;
async fn learn_song() {
// 是不是由于Sleep并没有让当前线程阻塞 只是 线程休眠 导致输出结果一样的 使用tokio
//sleep(Duration::from_secs(5));
// for _ in 0..10000 {
//
// }
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
println!("learn song");
}
async fn sing_song() {
println!("sing song");
}
async fn dance() {
println!("dance");
}
// 先学会唱歌,唱歌 后在跳舞
async fn learn_and_sing_song() {
learn_song().await; // 阻塞后续代码执行 await
sing_song().await;
}
async fn async_main(){
let f1 = learn_and_sing_song();
let f2 = dance();
futures::join!(f1, f2); // 使用 join 方法进行了阻塞
}
fn main() {
let runtime = Runtime::new().unwrap();
runtime.block_on(async_main());
println!("Hello, world!");
}
H:/programe/rust/.cargo/bin/cargo.exe run --color=always --package async_move --bin async_move
Finished dev [unoptimized + debuginfo] target(s) in 0.16s
Running `target\debug\async_move.exe`
dance
learn song
sing song
Hello, world!
Process finished with exit code 0
在高并发编程中有两种方式 1. 采用多线程 2. async 异步编程(js , 只有一个线程,但是使用async 框架)
总之,async编程并没有比多线程更好,最终还是根据你的使用场景作出合适的选择,如果无需高并发,或者也不在意线程切换带来的性能损耗,那么多线程使用起来会简单、方便的多!最后再简单总结下:
- 有大量 IO 任务需要并发运行时,选 async 模型
- 有部分 IO 任务需要并发运行时,选多线程,如果想要降低线程创建和销毁的开销,可以使用线程池
- 有大量 CPU 密集任务需要并行运行时,例如并行计算,选多线程模型,且让线程数等于或者稍大于 CPU 核心数
- 无所谓时,统一选多线程