Rust:Future、async 异步代码机制示例与分析

0. 异步、并发、并行、进程、协程概念梳理

Rust 的异步机制不是多线程或多进程,而是基于协程(或称为轻量级线程、微线程)的模型,这些协程可以在单个线程内并发执行。这种模型允许在单个线程中通过非阻塞的方式处理多个任务,从而实现高效的并发。

关于“并发”和“并行”的区别,这是两个经常被提及但含义不同的概念:

  • 并发(Concurrency):指的是同时处理多个任务的能力,这些任务可能在同一时间点开始、执行或结束,但不一定同时在物理硬件上执行。在单线程环境中,通过切换任务(例如,通过协程或事件循环)可以实现并发。

  • 并行(Parallelism):指的是同时执行多个任务,通常是在不同的处理单元(如CPU核心)上同时进行。这确实需要多线程或多进程环境,或者利用GPU、TPU等其他并行处理硬件。

Rust的异步机制非常适合处理高并发的I/O密集型任务,因为它可以有效地利用单个线程来处理多个任务,避免线程切换的开销,并通过非阻塞I/O减少等待时间。然而,它并不直接提供并行计算能力,即在多个物理处理单元上同时执行任务的能力。

对于需要并行计算能力的场景(例如,CPU密集型任务,如科学计算、大数据分析或图形渲染),Rust程序员通常会使用其他方法,如:

  • 使用多线程:通过Rust的标准库中的thread模块或其他并发原语(如crossbeamrayon等第三方库)来创建和管理多个线程。

  • 利用并行计算库:例如,使用OpenMP绑定的rust-omp库,或者使用专门为并行计算设计的语言和框架。

  • GPU加速:对于某些特定类型的计算密集型任务,可以利用GPU的并行处理能力,这通常需要使用专门的库(如cuda-sys用于CUDA编程)。

因此,虽然Rust的异步机制非常适合处理高并发的I/O密集型任务,但它本身并不直接支持并行计算。对于需要并行处理能力的场景,Rust程序员需要结合其他技术或库来实现。

1. Future

在Rust中,使用Future trait通常与.await语法结合,使得异步代码的编写和理解更加直观。以下是一个简单的例子,展示了如何定义一个实现Future trait的结构体,并使用.await来等待它的完成。

首先,请注意,直接实现Future trait是比较低级的操作,通常你会使用像async函数这样的高级抽象来间接创建Future。不过,为了教学目的,下面是一个简单的Future实现示例:

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};// 定义一个简单的Future结构体
struct SimpleFuture {value: Option<i32>,
}// 为SimpleFuture实现Future trait
impl Future for SimpleFuture {type Output = i32;fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {// 在这个例子中,我们简单地假设Future已经完成,并返回一个固定的值if let Some(value) = self.value.take() {Poll::Ready(value) // 表示Future已经完成,并返回结果} else {Poll::Pending // 表示Future还未完成}}
}// 创建一个异步函数,它使用我们定义的SimpleFuture
async fn async_main() {// 创建一个新的SimpleFuture实例,这里我们初始化value为Some(42),意味着这个Future已经完成let future = SimpleFuture { value: Some(42) };// 使用.await语法等待Future完成,并获取结果let result = future.await;println!("Future result: {}", result);
}fn main() {// 在实际的Rust程序中,你需要一个运行时来执行异步代码,如tokio或async-std。// 这里为了简化,我们仅展示了Future的定义和使用,没有涉及具体的运行时。// 下面的代码块是伪代码,表示如何在一个异步运行时中执行async_main函数。// 假设的异步运行时执行函数// run_async(async_main()).unwrap();
}// 注意:要运行上面的异步代码,你需要集成一个异步运行时(如tokio或async-std),
// 并且使用适当的函数来启动异步任务。上面的`main`函数仅作为示意,并非实际可运行的代码。

2. async

在上面的代码中,SimpleFuture结构体实现了一个最简单的Future。然而,在实际应用中,你几乎总是会使用async函数和.await语法来编写异步代码,而不是直接实现Future trait。async函数会自动生成一个实现Future trait的类型,你无需手动实现它。

例如,下面是一个使用async函数的更实用的例子:

use std::time::Duration;#[tokio::main] // 使用tokio运行时
async fn main() {let result = do_something_async().await;println!("Async operation result: {}", result);
}async fn do_something_async() -> i32 {// 模拟一个耗时的异步操作,比如网络请求或数据库查询tokio::time::sleep(Duration::from_secs(1)).await; // 等待1秒42 // 返回某个结果
}

在这个例子中,do_something_async是一个async函数,它返回一个Future,该Future在1秒后解析为值42。我们使用tokio运行时来执行这个异步代码。注意,为了运行此代码,你需要在你的Cargo.toml中添加tokio作为依赖项。

3. 如何在异步代码执行期间做其他工作

在Rust的异步编程中,如果你想在do_something_async执行期间做其他事情,你可以使用tokio::spawn来并发地运行多个异步任务。这样,你可以在等待一个任务完成的同时执行其他任务。下面是一个示例代码,展示了如何在do_something_async执行期间执行另一个异步任务:

use std::time::Duration;
use tokio::task;#[tokio::main]
async fn main() {// 使用tokio::spawn并发运行do_something_asynclet handle = task::spawn(do_something_async());// 在此期间,你可以执行其他任务println!("Doing something else while waiting for the async operation...");tokio::time::sleep(Duration::from_secs(2)).await; // 模拟其他耗时操作,比如等待2秒println!("Other task completed.");// 现在等待之前的异步操作完成并获取结果let result = handle.await.unwrap(); // handle.await 返回 Result<T, JoinError>,我们需要unwrap来获取结果println!("Async operation result: {}", result);
}async fn do_something_async() -> i32 {// 模拟一个耗时的异步操作tokio::time::sleep(Duration::from_secs(3)).await; // 假设这个操作需要3秒42 // 返回结果
}

在这个例子中,do_something_async函数被tokio::spawn调用,从而在一个单独的异步任务中执行。主任务在do_something_async执行的同时,进行了一些其他操作(在这个例子中,是等待了2秒钟)。之后,主任务通过handle.await等待do_something_async的完成,并获取其结果。

请注意,tokio::spawn返回的是一个JoinHandle,它代表了一个独立的异步任务。通过在这个JoinHandle上调用.await,你可以等待该任务完成,并获取其返回值(如果存在的话)。在这个例子中,do_something_async返回一个i32类型的值,所以我们通过handle.await.unwrap()来获取这个值。如果异步任务在执行过程中发生了panic,那么handle.await会返回一个Err(JoinError::Panic(...))

4. tokio::spawn

那么上面例子中,tokio::spawn 是通过启动新线程实现的异步机制吗?

在Tokio中,tokio::spawn并不会直接启动新的线程。相反,它会在Tokio的运行时中异步地调度和执行任务。Tokio的运行时是一个基于异步I/O和多任务调度的系统,它通常使用少量的线程(通常与系统的CPU核心数相匹配)来高效地处理大量的并发任务。

当你调用tokio::spawn时,你提交的任务会被放入Tokio的任务队列中,等待被运行时中的某个工作线程执行。这些工作线程是Tokio运行时在初始化时创建的,并且会被复用来执行多个任务。因此,tokio::spawn本身不会为每个任务创建新的线程,而是利用现有的线程池来并发执行多个任务。

这种基于异步I/O和事件驱动的并发模型,使得Tokio能够在少量线程上高效地处理大量并发连接和任务,从而实现高吞吐量和低延迟的I/O操作。

5. 同一线程内的多个异步任务

给个具体例子:

use tokio::join; // 用于同时等待多个异步任务完成#[tokio::main] // 使用Tokio运行时
async fn main() {// 定义两个异步任务let task1 = async {// 模拟异步操作,比如网络请求或文件读取tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;println!("Task 1 completed");1 // 返回任务结果};let task2 = async {// 另一个模拟的异步操作tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;println!("Task 2 completed");2 // 返回任务结果};// 使用join!宏来等待两个任务同时完成let (result1, result2) = join!(task1, task2);// 输出结果println!("Result of task 1: {}", result1);println!("Result of task 2: {}", result2);
}

在上面的例子中,我们定义了两个异步任务task1task2,每个任务都会等待一段时间(模拟异步I/O操作)然后打印一条消息并返回一个结果。通过使用join!宏,我们可以同时等待这两个任务完成,并且获取它们的结果。

当你运行这个程序时,你会看到task2task1更早完成,因为它等待的时间更短。这展示了如何在单个线程中通过Tokio运行时并发执行多个异步操作。

请注意,虽然这些异步任务在逻辑上是并发的,但它们实际上是在单个线程上通过协程切换来执行的。Tokio运行时负责调度这些任务,使它们能够高效地共享线程资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/32661.shtml

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

相关文章

关于微信没有接入鸿蒙NEXT的思考

6月21日,纯血鸿蒙发布,国内的质疑声终于停止,不再被人喊叫换皮 Android 了.就连编程语言都是华为自研的。 可是发布会后微信却成了热点,因为余承东在感谢了一圈互联网企业,如:淘宝、支付宝、美团、京东、抖音、今日头条、钉钉、小红书、微博、B站、高德、WPS等等. 唯独没有感…

CSS基础学习记录(5)

目录 1、CSS语法 2、实例 3、CSS注释 4、id 选择器 5、class 类选择器 6、标签选择器 7、内联选择器 1、CSS语法 CSS 规则由两个主要的部分构成&#xff1a;选择器&#xff0c;以及一条或多条声明: 选择器&#xff08;Selector&#xff09;通常是您需要改变样式的 HTML …

Altera不同系列的型号命名规则

Altera芯片型号&#xff1a;10AX07H4F34I3SG 20nm工艺 资源&#xff1a; 大数据 云计算 人工智能 图像处理 MSEL

高级人工智能复习 中科大

参考&#xff1a; 中科大2023春季【高级人工智能】试题回顾 中国科学技术大学《高级人工智能》课程 重要知识点提纲 高级人工智能复习提纲 1.搜索 1.1 搜索问题的概念 搜索问题的五个要素&#xff1a;状态空间、后继函数、初始状态、目标测试和路径耗散。 用状态图描述搜索…

Codeforces Round 953 (Div. 2) A~F

A.Alice and Books&#xff08;思维&#xff09; 题意&#xff1a; 爱丽丝有 n n n本书。第 1 1 1本书包含 a 1 a_1 a1​页&#xff0c;第 2 2 2本书包含 a 2 a_2 a2​页&#xff0c; … \ldots …第 n n n本书包含 a n a_n an​页。爱丽丝的操作如下&#xff1a; 她把所有的…

C语言之常用标准库介绍

文章目录 1 标准库1.1 诊断assert.h1.2 字符类别测试ctype.h1.3 错误处理errno.h1.4 整型常量limits.h1.5 地域环境locale.h1.6 数学函数math.h1.7 非局部跳转setjmp.h1.8 可变参数表stdarg.h1.9 公共定义stddef.h1.10 输入输出stdio.h1.11 实用函数stdlib.h1.12 日期与时间函数…

L57---112.路径总和(广搜)---Java版

1.题目描述 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。叶子节点 是指…

c++新技术

C的新技术主要体现在其不断演进的新标准中&#xff0c;这些新标准为开发者提供了更多的工具和特性&#xff0c;使得C成为一门现代化的高效编程语言。以下是关于C新技术的一些关键点和概述&#xff1a; C新标准&#xff1a; C11&#xff1a;引入了众多现代C的特性&#xff0c;如…

Lambda函数(箭头表达式)

箭头函数 也被称为Lambda函数或者箭头表达式&#xff0c;是ECMAScript 2015&#xff08;又称ES6&#xff09;中新增的一种函数定义方式。它使用箭头&#xff08;>&#xff09;来定义函数&#xff0c;通常用于定义匿名函数或回调函数。 优点 箭头函数有以下几个显著特点&a…

力扣每日一题 6/23 字符串/模拟

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 520.检测大写字母【简单】 题目&#xff1a; 我们定义&#xff0c;在以下…

面试必考题 CSS的居中布局

CSS居中对齐的几种方式 使用flex布局利用css3中的flex弹性盒的属性利用子元素auto 使用grid布局使用定位使用定位与margin利用CSS3属性transform 使用文本对齐使用table布局 使用flex布局 利用css3中的flex弹性盒的属性 .parent {display: flex;justify-content: center;alig…

经典面试题【作用域、闭包、变量提升】,带你深入理解掌握!

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是前端菜鸟的自我修养&#xff01;今天给大家分享经典面试题【作用域、闭包、变量提升】&#xff0c;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;原创不易&#xff0c;如果能帮助到带大家&#xff0…

欧洲杯赛况@20240622

点击标题下「蓝色微信名」可快速关注 斯洛伐克对阵乌克兰&#xff0c;场面上大开大合&#xff0c;施兰茨连场进球&#xff0c;但这次乌克兰丢球后没泄气&#xff0c;连进两球反超&#xff0c; 从数据统计上&#xff0c;两队不相上下&#xff0c;这个小组的形势更加复杂了&#…

【ajax核心05】宏任务与微任务

ES6之后引入Promise对象(用来管理异步任务)&#xff0c;让JS引擎也可以发起异步任务 一&#xff1a;异步任务分类 异步任务分为&#xff1a;宏任务与微任务 宏任务 由浏览器环境执行的异步代码 具体宏任务分类 微任务 由JS引擎执行的代码 创建Promise对象时&#xff0c;…

Java中的Servlet编程:从基础到高级应用

Java中的Servlet编程&#xff1a;从基础到高级应用 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;在本文中&#xff0c;我们将探讨Java中的Servlet编程&#x…

[JS]语句

介绍 代码分类 表达式: 可以被求值的代码称为表达式, 可以用于赋值 语句: 一段可执行的代码, 可以有结果, 可以没有结果, 不能用于赋值 语句分类 代码从上往下执行, 称为顺序结构代码按照条件选择执行, 称为分支结构某些代码被重复执行, 成为循环结构 分支结构 if分支 if…

python-今年第几天

[题目描述] 定义一个结构体变量&#xff08;包括年、月、日&#xff09;。 计算该日在本年中是第几天&#xff0c;注意闰年问题。输入格式&#xff1a; 年 月 日。输出格式&#xff1a; 当年第几天。样例输入 2000 12 31样例输出 366 数据范围 对于100%的数据&#xff0c;保…

【C++PCL】点云处理Kd-tree原理

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 重点:每个模块都有参数如何调试的讲解,即调试某个参数对结果的影响是什么,大家有问题可以评论哈,如果文章有错误的地方,欢迎来指出错误的…

【前端vue3】TypeScrip-基础类型和任意类型

安装TypeScript npm install typescript -g 运行tsc -v可查看当前版本 为了方便调试可以安装 先安装小满zs大神写的工具&#xff0c;可以切换下载源 npm i xmzs -g 安装成功后 使用mmp ls查看当前的源有哪些 使用 mmp use选择镜像源 切换成功后&#xff0c;安装TypeScrip…

C#的膨胀之路:创新还是灭亡

开篇概述 C#&#xff0c;这门由微软推出的编程语言&#xff0c;自2000年诞生以来&#xff0c;以其简洁的语法、强大的功能和广泛的应用场景&#xff0c;赢得了我等程序员的热爱。它在.NET框架的加持下&#xff0c;展现出无与伦比的开发效率和性能。然而&#xff0c;随着时间的流…