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

前言

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

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

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

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

关键功能

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宏等价

// 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

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

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.mzph.cn/web/4261.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么理解React Router

React Router就是实现不用刷新的条件下切换不同页面。路由的本质是页面URL发生改变,页面的显示结果也发生改变,但是页面不会刷新。 React Router分为几个部分: React-Router:实现了路由核心部分功能;React-Router-dom:基于react-router,加入了在浏览器运行环境下的一些…

Qt设置可执行程序图标,并打包发布

一、设置图标 图标png转ico: https://www.toolhelper.cn/Image/ImageToIco设置可执行程序图标 修改可执行程序图标 添加一个rc文件,操作如下,记得后缀改为rc 打开logo.rc文件添加代码IDI_ICON1 ICON DISCARDABLE "logo.ico"在项目pro后缀名的文件中添加代码 RC_…

一套Java语言开发的(药物不良反应智能监测ADR成品源码)B/S架构:用于监测和收集药品在使用过程中发生的不良反应的系统

药品不良反应监测工作弥补了药品上市前研究的局限性&#xff0c;可以最大程度上降低药品不良反应的重复发生&#xff0c;提高合理用药水平&#xff0c;同时也为监管、遴选、调整基本药物提供了数据支持。 所谓“是药三分毒”&#xff0c;药品是把双刃剑&#xff0c;具有两面性…

基于springboot+vue+Mysql的CSGO赛事管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Docker容器部署overleaf

overleaf在线版限制很多&#xff0c;好在开源&#xff0c;准备在本地Docker部署&#xff0c;网上翻了翻&#xff0c;似乎本地部署并非易事&#xff0c;我也尝试了一下&#xff0c;发现直接使用docker-compose拉官方最新镜像部署的确问题很多&#xff0c;不过最终还是完美解决。…

【ARMv9 DSU-120 系列 4.2 -- Utility Bus 访问的 memory-mapped 寄存器详细介绍】

请阅读【Arm DynamIQ™ Shared Unit-120 专栏 】 文章目录 External registers关于Cluster系统控制寄存器的关键点摘要表概述Cluster registers summaryExternal registers ARMv9架构中的DSU-120(DynamIQ™ Shared Unit-120)提供了一组Cluster系统控制寄存器,这些寄存器可以…

36 ajax的相关属性和api

如果是get方式&#xff0c;请求参数携带在url中 url?key1value1&key2value2..... 如果是post方式&#xff0c;请求参数携带在send中&#xff0c;key1value&key2value..... 异步 readyState是xhr对象不同步骤的状态码 0 new了一个XMLHttpRequest对象 1 调用了open…

Git 流程和命令

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

vs2019 - warning LNK4098 : 默认库“msvcrt.lib”与其他库的使用冲突

文章目录 vs2019 - warning LNK4098 : 默认库“msvcrt.lib”与其他库的使用冲突概述笔记实验 - 编译静态库实验 - 编译主工程&#xff0c;包含静态库实验主工程和静态库编译设置不同时的编译报错和警告备注备注 - 判断/Mdd, /MdEND vs2019 - warning LNK4098 : 默认库“msvcrt.…

OpenHarmony实战开发-动画概述

UI&#xff08;用户界面&#xff09;中包含开发者与设备进行交互时所看到的各种组件&#xff08;如时间、壁纸等&#xff09;。属性作为接口&#xff0c;用于控制组件的行为。例如&#xff0c;开发者可通过位置属性调整组件在屏幕上的位置。 属性值的变化&#xff0c;通常会引…

NetSuite如何迁移Workbook

截止到24.1&#xff0c; Workbook的迁移仍然是个难题。Bundle和Copy To Account都不支持Workbook的迁移&#xff0c;只剩下SDF的办法了。所以&#xff0c;今朝我们就介绍一下SDF迁移Workbook的方法。 1. 迁移准备 基本上&#xff0c;Workbook的迁移涉及4类对象&#xff1a; …

VMWARE安装xpsp3时无法从光盘启动安装显示dhcp转圈圈

VMWARE安装xpsp3时无法从光盘启动安装显示dhcp转圈圈。之前都安装过无数次了。这次被卡住了。 原来是这里原因 不成功是因为启动时连接没打钩。默认是打钩的。不知道怎么的我把勾去掉了。我还跑去设置bios。都没有用。

paddle ocr模型量化实践

参考&#xff1a;https://github.com/PaddlePaddle/PaddleOCR/blob/main/deploy/slim/quantization/README.md https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7.1/doc/doc_ch/FAQ.md 蒸馏 剪枝 量化 参考&#xff1a;https://blog.csdn.net/mddCSDN/article/de…

ptyhon画图显示中文

import matplotlib.pyplot as plt import matplotlib# 设置中文字体 matplotlib.rcParams[font.sans-serif] [SimHei] matplotlib.rcParams[font.family]sans-serifplt.plot([1, 2, 3, 4]) plt.xlabel(这是x轴) plt.ylabel(这是y轴) plt.title(这是标题) plt.show()用这个代码…

记一次线上日志堆栈不打印问题排查(附:高并发系统日志打印方案可收藏)

目录 一.线上的日志堆栈不打印了二.一步一步仔细排查三.最后搞定四.聊一聊线上日志到底应该怎么打印4.1 日志打印的诉求4.2 常见的系统日志上报方案4.2.1 ELK 方案4.2.2 自定义log appender 完成应用日志采集. 4.3 日志常见框架傻傻分不清4.4 日志在高并发系统中需要注意的 tip…

每日一题:最大子数组和

给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xff1a;6…

Docker 的数据管理 端口映射 容器互联 镜像创建

一 Docker 的数据管理 1 管理 Docker 容器中数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09; 数据卷容器&#xff08;DataVolumes Containers&#xff09;。 1.1 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机…

江开2024年春《计算机组成原理 060214》第4次计分作业参考答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 单选题 1某计算机字长32位&#xff0c;其存储容量为4GB&am…

【C++】 dlopen 和dlsys

最近项目&#xff0c;发现了dlopen的使用&#xff0c;所以总结一下 什么是dlopen和dlsym dlopen是一款Unix系列操作系统下的动态链接库函数&#xff0c;它的主要功能是在程序运行时动态加载指定的共享库&#xff08;也可称为动态链接库&#xff09;dlsym是从库中查找函数或者…

STM32F103 USB 端口的电阻配置

参考ST 官方的STLINK V2 电路图&#xff0c;如下&#xff1a; STLINK V2 的主控是STM32F103CBT6&#xff0c;USB 端口电路在左下角。DP&#xff0c;DM 上串的是0 Ω 电阻&#xff0c;所以103 系列芯片应该是内置了匹配电阻&#xff0c;外置的22Ω 可以去掉了。DP 外接了1.5K 上…