Rust 实战练习 - 11. Rust异步的基石 tokio

news/2024/10/21 9:16:41/

前言

Tokio是一个异步运行时。同时支持embedded devices.

  • 对异步代码的多线程运行时
  • 对标准库的异步实现 (这个可以省很多事情)
  • 生态系统丰富,非常多的工具库实现

Tokio不是万能的,部分场景不建议,可以考虑使用其他的:

  • 多CPU计算密集型,并行计算。Tokio主要解决多个任务IO等待问题,对于并行计算没有太大优势。
  • 大量文件读取。Tokio没有提供异步文件API. 使用它与普通线程读取文件没有区别。
  • 单个Web请求,或者阻塞型请求。因为Tokio优势在多请求下的异步处理,简单场景有没有优势。

关键功能

rust">use tokio::net; // 提供 TCP/UDP 等的异步实现
use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; // read/write 异步实现, 异步io copy等
use tokio::fs::File; // 异步操作文件的实现
use tokio::process; // 异步的多进程实现
use tokio::time; // 时间的异步实现
use tokio::sync::Mutex;  // 支持异步的mutex, 替代 use std::sync::Mutex;// 异步channel模型
// mpsc 多生产,单消费
// oneshot 单生产,单消费
// broadcast 多对多
// watch 单生产,多消费
use tokio::sync::{mpsc, oneshot, broadcast, watch}; 
tokio::select!{}  // 对多个channel同时进行loop的宏// 异步main函数与宏
#[tokio::main]
async fn main(){}tokio::spawn(async move { ... }).await; // tokio::task::spawn 绿色线程task.

tokio::main宏等价

rust">// method 1
tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async {println!("Hello world");})// method 2
let rt = Builder::new_current_thread().enable_all().build().unwrap();rt.block_on(async move {while let Some(task) = recv.recv().await {tokio::spawn(handle_task(task));}// Once all senders have gone out of scope,// the `.recv()` call returns None and it will// exit from the while loop and shut down the// thread.
});

实现一个自定义Future

rust">use std::{env, future::Future, task::Poll, thread, time::{Duration, Instant}};
use tokio;struct Delay {when: Instant,
}impl Future for Delay {type Output = &'static str;// poll 实现的是一个状态机fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {if Instant::now() >= self.when {println!("time is over!");Poll::Ready("done")}else{println!("time not ready...");// 启动一个唤醒task// 如果没有这个waker,future会一直在pending状态,恢复不回来let waker = cx.waker().clone();let when = self.when;thread::spawn(move || {let now = Instant::now();if now < when {thread::sleep(when-now);}waker.wake();});Poll::Pending}}
}#[tokio::main]
async fn main(){let f = Delay{when: Instant::now()+Duration::from_millis(100)};println!("{:?}", f.await);
}

TcpServer+Client的tokio版本

之前我们借助async-std已经实现了基础的tcp server和client. 这里我们使用tokio.

[dependencies]
tokio = {version = "*", features = ["full"]}

code

rust">use std::{env, thread, time::Duration};
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, time::sleep};#[tokio::main]
async fn main() {let args = env::args().into_iter().collect::<Vec<_>>();if args.len()>1 {match args[1].as_str() {"-s" => tcp_server().await,"-c" => tcp_client().await,_ => println!("unknown cmd {}", args[1]),}}else{println!("Usage:\r\n\t-s Open Tcp Server.\r\n\t-c Open Tcp client to connect the server.")}
}async fn tcp_server() {let s = TcpListener::bind("0.0.0.0:8000").await.unwrap();println!("Listen on addr: {}\r\n=============", s.local_addr().unwrap().to_string());loop{let (c, _ ) = s.accept().await.unwrap();// Tokio 任务是一个异步绿色线程。非常轻量级。// 使用tokio内部调度程序分配,并不一定在新的线程tokio::spawn(async move {handler_tcp(c).await;});}
}async fn handler_tcp(mut c: TcpStream) {let mut buf = [0u8;1024];let info = format!("[{:?}] => client in: {}", thread::current().id(), c.peer_addr().unwrap().to_string());let n = c.read(&mut buf).await.unwrap();println!("Read Len: {} \r\n{}", n, String::from_utf8_lossy(&buf[..n]));// 模拟长时间耗时操作sleep(Duration::from_secs(8)).await;_ = c.write(format!("HTTP/1.1 200 OK\r\n\r\n{}\r\n", info).as_bytes()).await;println!("Peer Inf: {}\r\n========================", info);}async fn tcp_client() {let mut c = TcpStream::connect("127.0.0.1:8000").await.unwrap();c.set_nodelay(true).unwrap();_ = c.write("GET / HTTP/1.1\r\nAccept: */*\r\n\r\n".as_bytes()).await;let mut strbuf = String::new();_ = c.read_to_string(&mut strbuf).await;println!("resp: {}", strbuf);
}

一定要注意,所有的异步操作要使用await进行异步等待,否则这个调用并没有真正执行,达不到想要的效果。

多个task之间的数据共享

使用常规的arc等异步共享功能。


http://www.ppmy.cn/news/1440511.html

相关文章

【嵌入式AI开发】轻量级卷积神经网络MobileNetV2详解

前言:MobileNetV2网络先升维后降维,在降维时使用线性激活函数,带残差的Inverted bottleck模块,防止ReLU信息丢失。在图像分类、目标检测、语义分割等任务上实现了网络轻量化、速度和准确度的权衡。 回顾MobileNetV1的理论和MobileNetV2项目实战可查阅如下链接: 【嵌入式AI…

【学习记录】autoware标定相机与激光雷达外参

一、autoware选择 这里踩了好几个坑&#xff0c;首先autoware作为一个无人驾驶知名框架&#xff0c;其内部实际上是有两套标定的东西的&#xff0c;这一点绝大多数博客没有提到。其中最常用的是一个叫标定工具箱的东西&#xff0c;这个ros包已经在1.10往后的版本中被删掉了&am…

【UnityRPG游戏制作】RPG项目的背包系统商城系统和BOSS大界面

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

leetcode 221 最大正方形面积

示例 3&#xff1a; 输入&#xff1a;matrix [["0"]] 输出&#xff1a;0 # 最大正方形面积 def max_square(matrix):m len(matrix)n len(matrix[0])if m 0 or n 0::return Nonemax_side 1dp [[0] * (n 1) for _ in range(m 1)]for i in range(1, m 1):fo…

【IR 论文】Google 对通过 prompt LLM 做 Query Expansion 的工作

论文&#xff1a;Query Expansion by Prompting Large Language Models ⭐⭐⭐ Google Research, arxiv:2305.03653 论文速读 之前我在论文笔记 Query2doc 中介绍了信息检索&#xff08;IR&#xff09;以及 Query Expansion 的相关背景知识。 本篇文章是 Google 发表的关于对…

php 获取网页数据

PHP preg_match_all() 函数 | 菜鸟教程 //$file是file_get_contents()获取到网页所有内容&#xff1b; //$mat是接收正则匹配到的内容&#xff1b; $arr preg_match_all(/<li>(.*)<\/li>/U,$file,$mat);$arr $mat[0]; 通过preg_match_all获取到内容后&#xff…

Git 流程和命令

Git 流程和命令 1.clone 远程项目 git clone [url] // 将存储库克隆到本地 2.复制代码到新clone的目录 &#xff1a;将目录所有文档添加到暂存区 git add . // 添加当前目录下所有文件至暂存区 3.将文件添加到暂存区&#xff0c;再添加到本地仓库&#xff0c;并记录下备注&…

spring读取配置文件多种方式深入详解

Spring框架提供了多种读取配置文件的方式&#xff0c;包括基于XML配置、基于Java注解配置以及基于Spring Boot的自动配置。下面对这些方式进行深入详解&#xff1a; 基于XML配置 在传统的Spring应用程序中&#xff0c;可以使用XML配置文件来读取配置信息。通常使用context:pro…