Rust异步编程 Async Await 入门

news2024/12/24 21:25:51

Rust Async Await 入门

在这里插入图片描述

在本文中,我们将仔细研究 Rust 中的异步编程。到目前为止,我对 Rust 异步的体验主要是从 Stack Overflow 复制代码。本文旨在帮助您了解什么是异步代码以及如何有效地使用它。

什么是异步代码?

要了解什么是异步代码,我们首先来谈谈同步代码。

在同步代码中,语句按顺序运行:

println!("Hello World");
let cargo_toml_content = std::fs::read_to_string("Cargo.toml").unwrap();
println!("'Cargo.toml':\n{}", cargo_toml_content);

上述语句按照明确的顺序执行,从上到下一个接一个地执行。” 打印 Hello World ,然后读取并打印 Cargo.toml 的内容。

这种模式在正常操作下非常好 - 但有时代码需要在当前上下文在等待其他内容时暂停 - 这通常称为阻塞。换句话说,当一段代码被阻塞时,它实际上处于暂停状态,等待特定操作完成才能继续。例如,当等待文件系统、网络通信、数据库事务甚至一段时间过去时,就会发生这种情况。在此阻塞状态期间,程序保持空闲状态,无法同时执行其他任务。在前面的示例中,循环无法继续进行下一次迭代,直到上一次迭代中的请求完成为止。这可能会导致效率低下,尤其是在处理大量此类请求时。

在下面的示例中,每次循环迭代都会向 example.com 发出请求。

for index in 1..=100 {
    let result = sync_http_client.get(format!("www.example.com/items/{}", index));
}

这里的问题是 sync_http_client.get 被阻塞。发生阻塞的原因有很多:

  • 等待文件系统
  • 等待网络
  • 等待一些数据库事务
  • 等待一段时间发生
  • 其他情况。

当程序被阻塞时,它什么也不做,只是等待响应返回以继续执行。如果我们需要做其他事情——我们就会陷入困境。在此示例中,循环无法运行下一次迭代,直到前一个迭代中的请求完全完成。虽然发出和读取单个请求相对较快,但循环中的代码运行 100 次并发出 100 个请求,因此整个循环需要一段时间才能运行。

如果有一种方法可以启动其他请求而不必等待前一个请求完成,该怎么做呢?

这就是异步编程的用武之地。异步编程就是非阻塞。假设您订购了一辆山地自行车以供周末骑行。您无需将所有时间都花在门口等待送货 - 您可以继续生活,做任何事情。异步运行时允许您继续正在做的任何事情,并作为通知,在送货到达门口时将提醒您。

稍后我们将详细介绍如何编写异步,但本质是我们可以将循环更改为以下内容以启动 100 个请求,而不需要等待完成前一个请求

let mut handles = Vec::new();
for index in 1..=100 {
    let handle = tokio::spawn(
        async_http_client.get(format!("www.example.com/items/{}", index))
    );
    handles.push(handle);
}
for handle in handles {
    let result = handle.await;
}

并行和并发 (Parallelization and concurrency)

在我们进一步讨论之前,我们应该注意 异步不适用于cpu 密集的操作。它仅对数据来自比 RAM 更远的地方并且是IO密集型时 有利。并行对于 cpu密集型 较高的操作是有益的。

** 并行是同时运行多个事物。并发是同时处理多个事情。**

并发与并行的区别:
异步是为并发(Concurrency)而设计的。 Tokio 的默认运行时使用线程,因此我们也可以从并行化中受益。

在这里插入图片描述

  • 并发(Concurrent) 是多个队列使用同一个咖啡机,然后两个队列轮换着使用(未必是 1:1 轮换,也可能是其它轮换规则),最终每个人都能接到咖啡
  • 并行(Parallel) 是每个队列都拥有一个咖啡机,最终也是每个人都能接到咖啡,但是效率更高,因为同时可以有两个人在接咖啡

Benchmarking 测试基准

比较使用异步编写的示例与同步编写的相同示例 - 对于大量并发 Web 请求,异步版本比同步请求快约 60%,比为每个请求旋转线程 1

CommandMean [s]Min [s]Max [s]Relative
./sync1.070 ± 0.0131.0601.0851.65 ± 0.09
./threads0.787 ± 0.0070.7820.7951.22 ± 0.06
./async0.732 ± 0.0160.7210.7501.13 ± 0.06
./async_threads0.646 ± 0.0330.6120.6771.00

Rust 异步编程入门

Rust 没有运行时 2 ,因此没有标准执行器(至少目前如此)。有几种流行的执行器运行时。这些是像任何其他库一样的crate,因此您可以通过将它们添加到 Cargo.toml 来使用它们。对于这个演示,我们将选择 Tokio Rust (Tokio-rs) - https://tokio.rs/ 作为最受欢迎的执行器。存在其他运行时并优先考虑不同的事情。例如,async-std 专注于 Rust 标准库的异步版本,而 smol 专注于轻量级。总体而言,Rust 的设计目的是避免干扰,因此它可以让您选择运行哪个执行程序。

首先,我们将运行 cargo new 。然后将 tokio = { version = "1.19", features = ["full"] } 添加到 Cargo.toml (或者如果您安装了 Cargo-edit: cargo add tokio -F full

#[tokio::main]
async fn main() {
    println!("Hello from an async function");
}

Async functions 异步函数

在 Rust 中,包含异步操作的函数由 async 关键字标识。要声明这样的函数,只需在其前面加上 async 前缀,如下所示:

async fn do_thing() {
    let result = some_async_function().await;
    println!("{}", result);
}

在异步函数中,您可以使用 .await 。它被写到异步函数调用的结尾,它在非阻塞执行中起着至关重要的作用。当您使用 .await 时,它会暂时停止执行并获取实际结果值。

现在,让我们更深入一点。异步函数以及异步块返回 Futures。 Future 是一个返回 Poll 的函数。 Poll 有点像 ResultOption ,它有两种变体,一种是 最终结果,另一种是该值仍处于阻塞状态。 Future 是惰性的,有两种方法可以运行 future: tokio::spawn 立即生成并获取 JoinHandle.await 。 Rust 会警告 unawaited futures。

编写异步操作

让我们看看下面的代码片段:

let contents = tokio::fs::read("Cargo.toml").await;

在此代码片段中,您可能会对 tokio::fs::read 及其与 Rust 标准库中的 std::fs::read 函数的相似之处感到好奇。这就是 Tokio 证明其实用性的地方。 Tokio 提供了 Rust 标准库中同步输入和输出 (IO) 操作的异步对应项。具体来说, tokio::fs::read 表示异步文件读取操作。它的特别之处在于它的异步特性;它使您的程序能够读取文件内容而不阻塞其他任务。在等待文件读取完成时,您的程序可以继续同时执行其他任务。这种非阻塞行为是 Rust 异步编程的一个基本方面,可以保护您的程序在 IO 操作期间不会无响应。

Writing concurrency 并发写入

如前所述,阻塞调用的问题在于它们一次只允许运行一个任务。

let weather = client.get("https://api.darksky.net/forecast").await;
let news = client.get("https://api.nytimes.com/svc/topstories").await;

使用 tokio::join! ,我们可以同时发起两个请求并等待它们的结果。

let weather = client.get("https://api.darksky.net/forecast");
let news = client.get("https://api.nytimes.com/svc/topstories");
let (weather, news) = tokio::join!(weather, news).await;

tokio::join !同时启动多个异步任务,然后同时等待它们的结果。本质上,它同时启动天气和新闻请求,然后等待两个响应,而不是等待一个响应完成后再启动另一个响应。这种并发方法与顺序执行有很大不同,在顺序执行中,您首先请求天气,然后等待其完成,并且只有在请求新闻之后才执行。通过利用 tokio::join! ,您可以有效地利用程序的时间,从而提高处理多个异步操作时的性能。


为了保持这篇文章的简短和基础知识,我们将停在这里。如果您想了解有关编写异步的更多信息,可以阅读 Rust 异步官方书籍,并且 Tokio 有精彩的教程。

Conclusion 总结

Rust Async 是 Rust 语言的一个实用且不断发展的方面。虽然异步功能不断发展,但未来仍有改进的空间。您可以在 areweasyncyet.rs 上检查异步功能的当前状态以及异步生态系统的其他方面。这篇文章提供了编写异步 Rust 代码的介绍性指南,因此您绝对可以等待未来的文章,深入探讨 Rust 中的异步,主题包括: Rust 流、异步代码中的错误处理、高级并发模式以及实际应用程序中异步 Rust 的实际示例。


脚注

  1. 我们在展示异步的有益结果方面遇到了一些困难,并且仍然不确定这些结果是否很好地反映了异步的好处。您可以在此处查看结果,并在此处查看完整的基准测试代码here。 ↩

  2. 从技术上讲,有恐慌处理程序和The Rust runtime - The Rust Reference ↩

原文连接:Getting started with Async Rust

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

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

相关文章

openGauss使用BenchmarkSQL进行性能测试(上)

一、前言 本文提供openGauss使用BenchmarkSQL进行性能测试的方法和测试数据报告。 BenchmarkSQL,一个JDBC基准测试工具,内嵌了TPC-C测试脚本,支持很多数据库,如PostgreSQL、Oracle和Mysql等。 TPC-C是专门针对联机交易处理系统…

金融知识分享系列之:MACD指标精讲

金融知识分享系列之:MACD指标精讲 一、MACD指标二、指标原理三、MACD指标参考用法四、MACD计算步骤五、MACD分析要素六、根据快线DIF位置判断趋势七、金叉死叉作为多空信号八、快线位置交叉信号九、指标背离判断行情反转十、差离值的正负十一、差离值的变化十二、指…

K 近邻算法

为什么学习KNN算法 KNN是监督学习分类算法,主要解决现实生活中分类问题。 (1)首先准备数据,可以是视频、音频、文本、图片等等 (2)抽取所需要的一些列特征,形成特征向量 (3&…

iPhone, Android 手机是如何收到推送通知的?

本文转自 公众号 ByteByteGo,如有侵权,请联系,立即删除 iPhone, Android 手机是如何收到推送通知的? 我们的手机或电脑是如何收到推送通知的? 通常我们可以使用消息解决方案 Firebase 来支持通知推送。下图显示了 Fi…

云数据库Redis配置用户名密码连接

一般情况,生产环境6379端口是禁止对外开放的, 所有用户名密码可以不设置。 但是如果有格鲁需求,需要开放redis公网访问,建议端口限制IP,并设置用户密码 spring中配置 阿里云数据库 云数据库 Redis_缓存数据库_高并发_读写分离-阿里云 添加白名单 申请公网访问地址 配…

2024年共享WiFi项目到底怎么样?

共享WiFi项目是近年来兴起的一种新型商业模式,商家通过在自己店铺升级wifi链接模式使其数字化,让用户能够方便地连接到互联网,提升到店体验,增加线上引流。这一项目的出现,为人们的生活带来了诸多便利,同时…

基于SpringBoot的“实习管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“实习管理系统”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 学生注册界面图 后台登录界面图 …

1.Python是什么?——《跟老吕学Python编程》

1.Python是什么?——《跟老吕学Python编程》 Python是一种什么样的语言?Python的优点Python的缺点 Python发展历史Python的起源Python版本发展史 Python的价值学Python可以做什么职业?Python可以做什么应用? Python是一种什么样的…

LoadBalancer负载均衡服务调用

LoadBalancer负载均衡服务调用 1、Ribbon目前也进入维护 ​ Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。 ​ 简单的说,Ribbon是Netflix发布的开源项目,主要功能是**提供客户端的软件负载均衡算法和服务调用。**Ribbon…

自动备份文件:守护数据安全新利器

随着信息化时代的到来,文件已经成为我们日常生活和工作中不可或缺的一部分。然而,数据丢失或损坏的风险也随之而来,因此自动备份文件的重要性愈发凸显。自动备份文件不仅可以保护我们的宝贵数据,还可以在意外发生时迅速恢复&#…

Seata源码流程图

1.第一阶段分支事务的注册 流程图地址:https://www.processon.com/view/link/6108de4be401fd6714ba761d 2.第一阶段开启全局事务 流程图地址:https://www.processon.com/view/link/6108de13e0b34d3e35b8e4ef 3.第二阶段全局事务的提交 流程图地址…

Python | Logger通用日志记录器

一、代码 通用日志记录器,可以输出不同颜色的记录到控制台,并输出到指定文件夹下可以在不同py文件中同时使用,共用同一个记录器适用window或linux平台 #!/usr/bin/env python # -*- coding: utf-8 -*- import os import inspect import log…

镭速教你如何解决大数据量串行处理的问题

大数据的高效处理成为企业发展的关键。然而,大数据量串行处理的问题常常困扰着许多企业,尤其是在数据传输方面。本文将探讨大数据量串行处理的常见问题,并介绍企业常用的处理方式,最后重点阐述镭速如何提供创新解决方案&#xff0…

Claude3发布,将取代ChatGPT4?

目录标题 前言Claude简介Claude 3 的能力高级推理视觉分析代码生成多语言处理 性能比较 前言 一夜之间,全球最强 AI 模型易主。大模型行业再次经历变革。OpenAI 最大的竞争对手 Anthropic 发布了新一代 AI 大模型系列 ——Claude 3。该系列包含三个模型&#xff0c…

鸿蒙开发之MPChart图表开发

一、简介 随着移动应用的不断发展,数据可视化成为提高用户体验和数据交流的重要手段之一,因此需要经常使用图表,如折线图、柱形图等。OpenHarmony提供了一个强大而灵活的图表库是实现这一目标的关键。 在 ohpm 中心仓(https://ohpm.openharmony.cn/)中,汇聚了众多开发者…

【Python】新手入门学习:详细介绍接口分隔原则(ISP)及其作用、代码示例

【Python】新手入门学习:详细介绍接口分隔原则(ISP)及其作用、代码示例 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、Py…

c++中多种类型sort()排序的用法(数组、结构体、pair、vector)

c中多种类型sort排序的用法 一、对数组排序1、默认排序2、自定义排序 二、对结构体进行排序三、对pair进行排序1、默认排序2、自定义排序 四、对vector进行排序1、默认排序2、去重排序3、自定义排序 一、对数组排序 1、默认排序 默认从小到大进行排序 #include <bits/std…

Windows Server 各版本搭建终端服务器实现远程访问(03~19)

一、Windows Server 2003 左下角开始➡管理工具➡管理您的服务器&#xff0c;点击添加或删除角色 点击下一步 勾选自定义&#xff0c;点击下一步 蒂埃涅吉终端服务器&#xff0c;点击下一步 点击确定 重新登录后点击确定 点击开始➡管理工具➡计算机管理&#xff0c;展开本地…

Java算法总结之冒泡排序(详解)

程序代码园发文地址&#xff1a;Java算法总结之冒泡排序&#xff08;详解&#xff09;-程序代码园小说,Java,HTML,Java小工具,程序代码园,http://www.byqws.com/ ,Java算法总结之冒泡排序&#xff08;详解&#xff09;http://www.byqws.com/blog/3145.html?sourcecsdn 冒泡排序…

网址如何转静态二维码?扫码跳转链接的制作步骤

一般网址想要转换成可以长期使用的二维码&#xff0c;可以通过制作静态码的方式将链接网址转二维码图片使用。这种方式只是将网址从链接的形式转换成二维码的形式&#xff0c;只要添加的网址不失效&#xff0c;那么二维码是可以长期扫码展示内容的&#xff1f;那么如何制作网址…