Rust Async 异步编程(五):async/.await

Rust 基于 await、async 的异步编程和纤程、协程的实现 它使得在 Rust 中编写异步代码变得更加简单和直观,通过结合 async/await 语法,你可以编写出清晰易读的异步代码,提高程序的并发性能和响应能力。这意味着虽然异步操作本身不会启动新线程,但它们可以在现有的线程池中的线程上执行,从而实现高效的并发处理。async 关键字用于声明一个函数是异步的,这意味着该函数可以包含挂起执行的操作(如网络请求或文件读写),而不会阻塞整个程序的执行。异步操作的执行是由运行时库(如Tokio)管理的,它们可能会在线程池中的线程上调度这些操作,以实现高效的并发处理。 阅读详情

Rust Async 异步编程(五):async/.await

async/.await 是 Rust 语法的一部分,它在遇到阻塞操作时(例如 I/O)会让出当前线程的所有权而不是阻塞当前线程,这样就允许当前线程继续去执行其它代码,最终实现并发。

使用 async 的两种方法

有两种方式可以使用 async:

  • async fn用于声明函数
  • async { … } 用于声明语句块

它们会返回一个实现 Future trait 的值。

// `foo()`返回一个`Future<Output = u8>`,
// 当调用`foo().await`时,该`Future`将被运行,当调用结束后我们将获取到一个`u8`值
async fn foo() -> u8 { 5 }

fn bar() -> impl Future<Output = u8> {
    // 下面的`async`语句块返回`Future<Output = u8>`
    async {
        let x: u8 = foo().await;
        x + 5
    }
}

无需调整 async fn 的返回类型,Rust 自动把它当成相应的 Future 类型。返回 Future 包含所需相关信息:参数、本地变量空间…

Future 的具体类型由编译器基于函数体和参数自动生成:

  • 该类型没有名称
  • 它实现了 Future<Output=R>

async 是懒惰的,直到被执行器 poll 或者 .await 后才会开始运行,其中后者是最常用的运行 Future 的方法。 当 .await 被调用时,它会尝试运行 Future 直到完成,但是若该 Future 进入阻塞,那就会让出当前线程的控制权。当 Future 后面准备再一次被运行时(例如从 socket 中读取到了数据),执行器会得到通知,并再次运行该 Future ,如此循环,直到由 .awiat 完成解析。

这种途中能暂停执行,然后恢复执行的能力是 async 独有的。由于 await 表达式依赖于“可恢复执行”这个特性,所以 await 只能用在 async 里。

.await 做了什么?

  1. 获得 Future 的所有权,对其执行 poll
  2. 如果 Future Ready,其最终值就是 await 表达式的值,这时执行就可以继续了
  3. 否则就返回 Pending 给调用者

async 的生命周期

与传统函数不同,async fn 函数如果拥有引用或其他非 'static 类型的参数,那它返回的 Future 的生命周期就会绑定到参数的生命周期上,被这些参数的生命周期所限制。

这意味着 async fn 返回的 Future,在 .await 的同时,函数的引用或其他非 'static 类型的参数必须保持有效。

async fn foo(x: &u8) -> u8 { *x }

上面的函数跟下面的函数是等价的:

fn foo_expanded<'a>(x: &'a u8)
-> impl Future<Output = u8> + 'a {
    async move { *x }
}

意味着 async fn 函数返回的 Future 必须满足以下条件:当 x 依然有效时, 该 Future 就必须继续等待(.await),也就是说 x 必须比 Future 活得更久。

在一般情况下,在函数调用后就立即 .await 不会存在任何问题,例如 foo(&x).await。

但是,若 Future 被先存起来或发送到另一个任务或者线程,就可能存在问题了:

use std::future::Future;

fn bad() -> impl Future<Output = u8> {
    let x = 5;
    borrow_x(&x) // ERROR: `x` does not live long enough
}

async fn borrow_x(x: &u8) -> u8 { *x }

以上代码会报错,因为 x 的生命周期只到 bad 函数的结尾。 但是 Future 显然会活得更久:

error[E0597]: `x` does not live long enough
 --> src/main.rs:4:14
  |
4 |     borrow_x(&x) // ERROR: `x` does not live long enough
  |     ---------^^-
  |     |        |
  |     |        borrowed value does not live long enough
  |     argument requires that `x` is borrowed for `'static`
5 | }
  | - `x` dropped here while still borrowed

其中一个常用的解决方法就是将具有引用参数的 async fn 函数转变成一个具有 'static 生命周期的 Future。以上解决方法可以通过将参数和对 async fn 的调用放在同一个 async 语句块来实现:

use std::future::Future;

async fn borrow_x(x: &u8) -> u8 { *x }

fn good() -> impl Future<Output = u8> {
    async {
        let x = 5;
        borrow_x(&x).await
    }
}

如上所示,通过将参数移动到 async 语句块内, 我们将它的生命周期扩展到 'static, 并跟返回的 Future 保持了一致。

async move

async 允许我们使用 move 关键字来将环境中变量的所有权转移到语句块内,就像闭包那样,好处是你不再发愁该如何解决借用生命周期的问题,坏处就是无法跟其它代码实现对变量的共享:

// 多个不同的 `async` 语句块可以访问同一个本地变量,只要它们在该变量的作用域内执行
async fn blocks() {
    let my_string = "foo".to_string();

    let future_one = async {
        // ...
        println!("{my_string}");
    };

    let future_two = async {
        // ...
        println!("{my_string}");
    };

    // 运行两个 Future 直到完成
    let ((), ()) = futures::join!(future_one, future_two);
}



// 由于`async move`会捕获环境中的变量,因此只有一个`async move`语句块可以访问该变量,
// 但是它也有非常明显的好处: 变量可以转移到返回的 Future 中,不再受借用生命周期的限制
fn move_block() -> impl Future<Output = ()> {
    let my_string = "foo".to_string();
    async move {
        // ...
        println!("{my_string}");
    }
}

在多线程执行器上进行 .await

需要注意的是,当使用多线程 Future 执行器(executor)时, Future 可能会在线程间被移动,因此 async 语句块中的变量必须要能在线程间传递。 至于 Future 会在线程间移动的原因是:它内部的任何 .await 都可能导致它被切换到一个新线程上去执行。

由于需要在多线程环境使用,意味着 Rc、RefCell、没有实现 Send trait 的所有权类型、没有实现 Sync trait 的引用类型,它们都是不安全的,因此无法被使用。

需要注意,实际上它们还是有可能被使用的,只要在 .await 调用期间,它们没有在作用域范围内。

类似的原因,在 .await 时使用普通的、对 Future 无感知的锁也不安全,例如 Mutex。原因是,它可能会导致线程池被锁:当一个任务获取锁 A 后,若它将线程的控制权还给执行器,然后执行器又调度运行另一个任务,该任务也去尝试获取了锁 A ,结果当前线程会直接卡死,最终陷入死锁中。

因此,为了避免这种情况的发生,我们需要使用 futures 包下的锁 futures:🔒:Mutex 来替代 std::sync::Mutex 完成任务。

参考

  1. https://github.com/rustcn-org/async-book
  2. https://www.bilibili.com/video/BV1Ki4y1C7gj
【Rust异步编程】Rust异步编程原理解析、标准库实现与第三方库实现详解 异步编程是一种允许程序在等待某些操作(如I/O)完成时继续执行其他任务的编程范式。与传统的同步编程相比,异步编程可以显著提高程序的吞吐量和资源利用率。 在Rust中,异步编程具有以下特点: 非阻塞执行:任务可以在等待时让出控制权 轻量级:异步任务(称为future)比线程更轻量 显式标记:使用async/await语法明确标识异步代码 阅读详情

相关推荐

基于单片机的室内空气质量监控系统设计

摘 要 I Abstract II 引 言 1 1 控制系统设计 3 1.1 方案选择 3 1.2 系统控制原理 4 2系统硬件设计 5 2.1 单片机的选择与设计 5 2.2 温湿度模块设计 6 2.3 甲醛采集模块设计 8 2.4 显示器模块设计 9 2.5 按键模块设计 10 2.6 报警模块设计 11 2.7 加湿及风扇模块设计 11 3系统软件设计 13 3.1 总流程方案设计 13 3.2 温湿度获取的软件设计 14 3.3 液晶屏LCD1602的软件设计 15 4系统调试 13 4

Q_1928499906的博客 2702

一文读懂Rust的async

因为这些运行时之间有很多`相通`的地方,熟悉了一个再去熟悉其它的就简单了。就像我们学习编程语言一样,学好学深一门编程语言,再去学习其它的语言就快了。不要一开始就几门语言一起学,这样很可能实际开发时这也不行那也不行换来换去还是不能开发出东西.

一个不安分的程序员 9643

41-dify案例分享-基于database插件实现Text2sql的数据库查询图表工作流

本文介绍基于 Text2SQL 的 Dify 工作流实现过程。先阐述 Text2SQL 概念、步骤及应用领域。接着说明工作流制作,包括用 chatflow 加开场白、获取系统时间,配置 LLM 生成 SQL,用 SQL Execute 工具查询,经 LLM 汇总结果,代码生成 ECharts 数据,条件分支判断图表类型输出。最后介绍验证测试、提供体验地址,还提及借助工具生成建表和初始化数据 SQL 语句。

个人技术 1万+

Rust async,看这一篇就够了~

实战详细解释Rust Async,值得你看!

vince1998的博客 3491

多目标进化算法——NSGA-II(python实现)

NSGA-II(python实现)--最全注释,痴呆能懂--

no___good的博客 5281

Verilog简易电梯控制系统(2层)

电梯处于1楼,按KEY3,LED3亮,电梯处于上行状态时,立刻按 KEY2,LED2亮,电梯继续运行至2楼,LED3灭;按KEY3,LED3亮,电梯处于上行状态时,立刻按 KEY0,LED0亮,电梯继续运行至2楼,LED3灭;电梯处于2楼,按KEY2,LED2亮,电梯处于下行状态时,立刻按 KEY3,LED3亮,电梯继续运行至1楼,LED2灭;电梯处于2楼,按KEY2,LED2亮,电梯处于下行状态时,立刻按 KEY1,LED1亮,电梯继续运行至1楼,LED2灭;电梯运行至2楼后LED1灭,2楼待机。

m0_54240221的博客 6958

Rust : async、await 初探

rust 中的语法await!正式改成.await, 关于Future这套东东,很有必要了解。象actix-web也已经1.0了,里面其实也是到处Future这套。async、await这套东东,是rust的硬知识。 一、async和await的几种形式 1、async (1)、async fn 当然,也有很多情况,比如函数中有.await; 也可能没有.await的不同情况。这种也会导致,返回值...

代码过客 1万+

Linkage Mapper 工具参数详解——Building Network and Map Linkages

此文档用于解析使用LinkageMapper各输入输出参数详情以及可能的影响,并介绍了如何解释模型输出结果和输出参数,适合刚入手的人。篇幅很长很啰嗦,是因为每个参数都解释的万分细致。

sumuses的博客 6121

Rust异步编程async/.await原理解析(一)

在这个教程中我们将详细分析rust异步代码async/.await的内部运行机制。我们将使用async-std库而不是tokio,因为这是第一个支持async/.await语法的rust库。async/.await原理解析教程分为两部分,这是第一部分。 区块链开发教程链接:以太坊 | 比特币 | EOS | Tendermint | Hyperledger Fabric | Omni/USDT | Ripple 0、准备Rust练习环境 首先让我们先创建一个Cargo项目: ~$ cargo new -

新缸中之脑 1万+

RK3588开发笔记-ES8311音频芯片调试记录

本篇博客主要记录如何在RK3588平台试ES8311音频芯片的过程。这包括硬件连接、内核配置、驱动程序调试和测试步骤。通过以上步骤,我们成功在RK3588平台上调试了ES8311音频芯片。这包括硬件连接、内核配置、设备树配置、驱动加载以及音频测试。希望这篇调试记录能为其他开发者提供一些帮助。如果你有任何问题或建议,欢迎留言讨上调论。

flypig has a dream! 3165

Orange3实战教程:转换---预处理

部件提供了多种预处理方法,这些方法可以在单个预处理流程中组合使用。某些方法也可作为独立部件提供,支持更高级的技术和参数调整。连接到测试与评分:在交叉验证的每个批次中应用预处理器,随后学习器的预处理器会作用于预处理后的子集。连接到学习器:覆盖学习器的默认预处理流程,仅应用自定义流程(各学习器的默认步骤详见其文档)。直接连接到原始数据并输出预处理后的数据集,可能导致模型过拟合。:从原始数据中随机选择固定数量或百分比的特征,主要用于高级测试或教学用途。的预处理器输入,同时传入数据和学习器(如。

err2008的专栏 148

【Rust异步】async和await异步编程实战:高并发任务处理全解析

async 和 await 是 Rust 中用于异步编程的关键字。 async 用于定义异步函数,表示函数体中包含异步代码。 await 用于等待异步操作完成,并返回异步操作的结果。 • 异步函数使用 async 关键字定义,并返回实现了 Future Trait 的类型。 异步函数可以在其他异步函数中使用 await 关键字等待异步操作完成。 调用异步函数时,会返回一个实现了 Future Trait 的对象,可以通过调用.await 方法等待结果。

景天科技苑 2万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Rust 异步编程详解(async/await)

Rust异步编程通过async/await语法和Future trait实现高效I/O处理,核心机制包括状态机模型和运行时调度。文章详细解析了异步编程基础、核心组件(Future trait、async/await语法)、工作流程,并提供了HTTP请求等代码示例。同时探讨了错误处理、任务取消、Pin/Unpin等高级话题,比较了不同运行时的适用场景。最佳实践建议选择合适运行时、避免阻塞操作,并指出异步编程在性能和安全方面的优势,以及调试复杂度等限制。附带的并发网络请求示例展示了tokio运行时的实际应用,包

本博客分享软硬件一体化开发实战。硬件涵盖 PCB 设计、STM32 等 MCU 底层开发;软件包含 C++、Rust,Qt/QML、PyQt、Slint、TouchGFX、LVGL 各类 GUI 框架。方便软硬件开发者查阅学习。欢迎关注! 2223

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Rust 异步编程 async/await

Rust 的异步编程模型 async/await 提供了一种简洁、高效的方式来处理异步操作。它允许开发者以一种更自然和直观的方式来处理异步操作,同时保持了 Rust 的安全性和性能。通过 async/await,Rust 为异步编程提供了一流的语言支持,使得编写高效且可读性强的异步程序变得更加容易。

QQ672168的博客 1344

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Rust异步编程 Async Await 入门

要了解什么是异步代码,我们首先来谈谈同步代码。println!println!上述语句按照明确的顺序执行,从上到下一个接一个地执行。” 打印,然后读取并打印Cargo.toml的内容。这种模式在正常操作下非常好 - 但有时代码需要在当前上下文在等待其他内容时暂停 - 这通常称为阻塞。换句话说,当一段代码被阻塞时,它实际上处于暂停状态,等待特定操作完成才能继续。例如,当等待文件系统、网络通信、数据库事务甚至一段时间过去时,就会发生这种情况。在此阻塞状态期间,程序保持空闲状态,无法同时执行其他任务。

xuejianxinokok的专栏 1657

异步编程:Rust 的 async/await 详解

Rust异步编程模型通过async/await语法、Future Trait和异步运行时(Tokio/async-std)实现高效非阻塞I/O操作。async标记异步函数返回Future,await暂停但不阻塞线程,允许运行时调度其他任务。异步运行时管理任务调度和事件循环,Tokio提供完整的异步I/O支持。通过tokio::spawn和select!宏可实现并发任务处理。这种模型结合Rust的内存安全特性,使开发者能编写高性能、可维护的异步应用,特别适合I/O密集型场景。

2501_93870138的博客 993

Rust异步编程之async与.await

介绍了Rust中异步编程基本语法async与.await和Future,以及常见的运行时

飞鹰技术 1098

Rust Async 异步编程(一):入门

Rust Async 异步编程(一):入门

ProgramNovice的博客 1388

Rust异步编程的世界:为什么需要async/await?

文章摘要:本文探讨了Rust异步编程的必要性,对比了同步I/O的局限性和异步模型的高效性。通过代码示例展示了同步服务器的线程开销问题,并介绍了Rust async/await如何解决这些问题。文章分析了异步编程的核心优势:更高的并发性能、更低的内存消耗,以及async/await语法带来的可读性提升和一致的错误处理机制。技术验证基于Rust 1.70.0版本,证实了异步模型在处理高并发场景时的优越性。

lita527的博客 898

文章标题:深入探索 Rust 中的异步编程:使用 async 和 await 提高开发效率

在现代软件开发中,异步编程成为了处理并发任务和提高性能的重要手段。尤其是在高并发、高 I/O 的应用中,如何高效地进行并发控制是开发者面临的一大挑战。而在众多编程语言中,Rust 作为近年来广受关注的系统编程语言,其在内存安全和性能上的优势被许多开发者称赞。而异步编程在 Rust 中也得到了很好的支持,借助async和await关键字,我们可以方便地实现高效的异步编程。本文将深入介绍 Rust 中的异步编程机制,包括如何在 Rust 中使用async和await。

桂月二二博客 1260

futures-await源码剖析:探索Rust异步编程的早期实现

futures-await是Rust异步编程发展历程中的重要探索项目,它为Rust开发者提供了早期的async/await语法支持,是现代Rust异步生态系统的重要基石。本文将深入剖析futures-await的源码结构与核心实现,帮助开发者理解Rust异步编程模型的演进过程。 ## 项目核心架构解析 futures-await项目采用模块化设计,主要包含三大核心组件: - **运行时支持库

gitblog_01150的博客 803

快速入门futures-await:5分钟掌握Rust异步编程新语法

想要在Rust中编写简洁优雅的异步代码吗?futures-await正是你需要的终极解决方案!这个强大的库为Rust带来了async/await语法,让异步编程变得像同步代码一样简单直观。无论你是Rust新手还是经验丰富的开发者,只需5分钟就能掌握这种革命性的异步编程新语法。✨ ## 什么是futures-await? futures-await是一个Rust异步编程库,它通过`#[async

gitblog_00309的博客 772

掌握Rust异步编程:Futures与async/await实践指南

本文介绍了Rust语言的异步编程模型,重点讲解了Futures和async/await的使用。通过异步编程,可以提高程序在处理I/O密集型任务时的效率和响应速度。Rust的异步编程模型使得编写异步代码更加简洁易懂,有助于提升程序性能。

silenceallat的博客 1398

异步编程革命:Rust async/await完全指南

异步编程革命:Rust async/await完全指南 本文深入探讨了Rust异步编程的核心机制,从Future执行模型与任务调度机制、Pin和Unpin的内存安全原理,到Stream流处理与多Future并发技术,最后通过异步Web服务器实战开发展示完整应用。文章详细解析了Rust异步编程的底层实现原理和高级应用技巧,为开发者提供从基础到实战的完整指南。 Future执行模型与任务调度机制 在R...

gitblog_00027的博客 1262

CouchDB 的 FoundationDB 文档存储数据模型:JSON 文档如何映射为 KV 键值对

本文深度解析 Apache CouchDB 社区为 FoundationDB 后端设计的 JSON 文档存储数据模型(RFC 004:JSON document storage in FoundationDB)。你将理解 CouchDB 如何把任意 JSON 文档拆解为多条 KV 记录、如何编码叶子值与嵌套结构、如何在同一 keyspace 中保存多版本文档并实现 CRUD,以及 100KB 叶子

gitblog_00264的博客 965
上一篇: Rust Async 异步编程(四):Executor
下一篇: Rust Async 异步编程(六):Pin 和 Unpin
UestcXiye
博客等级 码龄8年 9031粉丝 1306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值