Rust Async 异步编程(二):Future trait

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

Rust Async 异步编程(二):Future trait

异步编程背后到底藏有什么秘密?究竟是哪只幕后之手在操纵这一切?如果你对这些感兴趣,就继续看下去,否则可以直接跳过,因为本章节的内容对于一个 API 工程师并没有太多帮助。

但是如果你希望能深入理解 Rust 的 async/.await 代码是如何工作、理解运行时和性能,甚至未来想要构建自己的 async 运行时或相关工具,那么本章节终究不会辜负于你。

自定义 Future trait

Future trait 是 Rust 异步编程的核心,毕竟异步函数是异步编程的核心,而 Future 恰恰是异步函数的返回值和被执行的关键。

首先,来给出 Future 的定义:它是一个能产出值的异步计算(虽然该值可能为空,例如 ())。实现了 Future 的类型表示目前可能还不可用的值。光看这个定义,可能会觉得很空洞,我们来看看一个简化版的 Future trait:

trait SimpleFuture {
    type Output;
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}

在上一章中,我们提到过 Future 需要被执行器 poll 后才能运行,诺,这里 poll 就来了,通过调用该方法,可以推进 Future 的进一步执行,直到被切走为止。

这里不好理解,但是你只需要知道 Future 并不能保证在一次 poll 中就被执行完,后面会详解介绍

若在当前 poll 中, Future 可以被完成,则会返回 Poll::Ready(result) ,反之则返回 Poll::Pending, 并且安排一个 wake 函数:当未来 Future 准备好进一步执行时, wake 函数会被调用,然后管理该 Future 的执行器(例如上一章节中的 block_on 函数)会再次调用 poll 方法,此时 Future 就可以继续执行了。

如果没有 wake 方法,那执行器无法知道某个 Future 是否可以继续被执行,除非执行器定期的轮询每一个 Future ,确认它是否能被执行,但这种作法效率较低。而有了 wake,Future 就可以主动通知执行器,然后执行器就可以精确的执行该 Future。 这种 “事件通知 -> 执行” 的方式要远比定期对所有 Future 进行一次全遍历来的高效。

也许大家还是迷迷糊糊的,没事,我们用一个例子来说明下。考虑一个需要从 socket 读取数据的场景:如果有数据,可以直接读取数据并返回 Poll::Ready(data), 但如果没有数据,Future 会被阻塞且不会再继续执行,此时它会注册一个 wake 函数,当 socket 数据准备好时,该函数将被调用以通知执行器:我们的 Future 已经准备好了,可以继续执行。

下面的 SocketRead 结构体就是一个 Future:

pub struct SocketRead<'a> {
    socket: &'a Socket,
}

impl SimpleFuture for SocketRead<'_> {
    type Output = Vec<u8>;

    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
        if self.socket.has_data_to_read() {
            // socket 有数据,写入 buffer 中并返回
            Poll::Ready(self.socket.read_buf())
        } else { // socket 中还没数据
            // 注册一个 wake 函数,当数据可用时,该函数会被调用,
            // 然后当前 Future 的执行器会再次调用 poll 方法,此时就可以读取到数据
            self.socket.set_readable_callback(wake);
            Poll::Pending
        }
    }
}

这种 Future 模型允许将多个异步操作组合在一起,同时还无需任何内存分配。不仅仅如此,如果你需要同时运行多个 Future 或链式调用多个 Future ,也可以通过无内存分配的状态机实现,例如:

trait SimpleFuture {
    type Output;
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}

/// 一个 SimpleFuture,它会并发地运行两个 Futur e直到它们完成
/// 之所以可以并发,是因为两个 Future 的轮询可以交替进行,一个阻塞,另一个就可以立刻执行,反之亦然
pub struct Join<FutureA, FutureB> {
    // 结构体的每个字段都包含一个 Future,可以运行直到完成.
    // 如果 Future 完成后,字段会被设置为 None,这样 Future 完成后,就不会再被轮询
    a: Option<FutureA>,
    b: Option<FutureB>,
}

impl<FutureA, FutureB> SimpleFuture for Join<FutureA, FutureB>
where
    FutureA: SimpleFuture<Output = ()>,
    FutureB: SimpleFuture<Output = ()>,
{
    type Output = ();
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
        // 尝试去完成一个 Future a
        if let Some(a) = &mut self.a {
            if let Poll::Ready(()) = a.poll(wake) {
                self.a.take();
            }
        }

        // 尝试去完成一个 Future b
        if let Some(b) = &mut self.b {
            if let Poll::Ready(()) = b.poll(wake) {
                self.b.take();
            }
        }

        if self.a.is_none() && self.b.is_none() {
            // 两个 Future 都已完成,可以成功返回了
            Poll::Ready(())
        } else {
            // 至少还有一个 Future 没有完成任务,因此返回 Poll::Pending,
            // 当该 Future 再次准备好时,通过调用 wake 函数来继续执行
            Poll::Pending
        }
    }
}

上面代码展示了如何同时运行多个 Future, 且在此过程中没有任何内存分配,让并发编程更加高效。 类似的,多个 Future 也可以一个接一个的连续运行:

/// 一个 SimpleFuture, 它使用顺序的方式,一个接一个地运行两个 Future
// 注意: 由于本例子用于演示,因此功能简单,AndThenFut 会假设两个 Future 在创建时就可用了
// 而真实的 Andthen 允许根据第一个 Future 的输出来创建第二个 Future,因此复杂的多
pub struct AndThenFut<FutureA, FutureB> {
    first: Option<FutureA>,
    second: FutureB,
}

impl<FutureA, FutureB> SimpleFuture for AndThenFut<FutureA, FutureB>
where
    FutureA: SimpleFuture<Output = ()>,
    FutureB: SimpleFuture<Output = ()>,
{
    type Output = ();
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
        if let Some(first) = &mut self.first {
            match first.poll(wake) {
                // 我们已经完成了第一个 Future,可以将它移除,然后准备开始运行第二个
                Poll::Ready(()) => self.first.take(),
                // 第一个 Future 还不能完成
                Poll::Pending => return Poll::Pending,
            };
        }

        // 运行到这里,说明第一个 Future 已经完成,尝试去完成第二个
        self.second.poll(wake)
    }
}

这些例子展示了在不需要内存对象分配以及深层嵌套回调的情况下,该如何使用 Future trait 去表达异步控制流。 在了解了基础的控制流后,我们再来看看真实的 Future trait 有何不同之处。

真实的 Future trait 定义

Rust 标准库中 Future trait 的定义:

trait Future {
    type Output;
    fn poll(
        // 首先值得注意的地方是,self 的类型从 &mut self 变成了 Pin<&mut Self>
        self: Pin<&mut Self>,
        // 其次将 wake: fn() 修改为 cx: &mut Context<'_>
        cx: &mut Context<'_>,
    ) -> Poll<Self::Output>;
}

首先这里多了一个 Pin,关于它我们会在后面章节详细介绍,现在你只需要知道使用它可以创建一个无法被移动的 Future ,因为无法被移动,因此它将具有固定的内存地址,意味着我们可以存储它的指针(如果内存地址可能会变动,那存储指针地址将毫无意义),也意味着可以实现一个自引用数据结构:struct MyFut { a: i32, ptr_to_a: *const i32 }。 而对于 async/await 来说,Pin 是不可或缺的关键特性。

其次,从 wake: fn() 变成了 cx: &mut Context<'_>。意味着 wake 函数可以携带(关于哪个 Future 调用了 wake 的)数据了,为何要携带数据?考虑一个真实世界的场景,一个复杂应用(例如 web 服务器)可能有数千连接同时在线,那么同时就有数千 Future 在被同时管理着,如果不能携带数据,当一个 Future 调用 wake 后,执行器该如何知道是哪个 Future 调用了 wake,然后进一步去 poll 对应的 Future?没有办法!那之前的例子为啥就可以使用没有携带数据的 wake?因为足够简单,不存在歧义性。

总之,在正式场景要进行 wake ,就必须携带上数据。 而 Context 类型通过提供一个 Waker 类型的值,就可以用来唤醒特定的的任务。

参考

  1. https://github.com/rustcn-org/async-book
  2. https://www.bilibili.com/video/BV1Ki4y1C7gj
闲置服务器之代理服务器篇 这台服务器是在阿里云免费白嫖的,天工开物计划,阿里给的300块无门槛优惠券,买了一台新加坡的境外服务器,288,刚刚抵扣完,因为是境外服务器,能够访问一些国内不能访问的网站,所以本文章仅从技术的角度来搭建代理服务器,使用时,请自觉遵守国家相关规定,如有任何违法行为,与本人无任何关系!!!。本文使用tinyproxy来搭建代理服务器。 阅读详情

相关推荐

深入解析LCD DRM驱动框架中的KMS与GEM机制

本文深入解析了Linux内核中DRM(Direct Rendering Manager)驱动框架的核心机制,重点剖析了KMS(内核模式设置)与GEM(图形执行管理器)的工作原理。KMS作为显示系统的总指挥,负责原子化地控制CRTC、Encoder、Plane等硬件资源,确保画面同步更新,避免撕裂;GEM则作为显存大管家,通过DUMB和PRIME机制高效管理图形缓冲区的分配、共享与同步。文章结合RK3568平台实战,阐述了如何利用这些机制优化显示性能,并提供了调试技巧与常见问题解决方案。

weixin_29188997的博客 388

Rust:有代价与无代价的Async Trait——从object safe开始

围绕object safe,讨论async-trait crate和官方新支持的async trait语法糖的原理、特性、应用与区别

qq_54714089的博客 4043

Xilinx IP核 Block Memory Generator v8.4 的使用

本文主要介绍如何使用并初始化 Xilinx 提供的IP核 Block Memory Generator v8.4`,为了确保成功初始化,还对其进行了一个简单的仿真,更多细节请参考官方手册。

高阶近似的博客 1万+

【Rust日报】2022-11-18 在 trait 中使用 `async fn`

在 trait 中使用async fnasync 工作组很高兴地宣布async fn现在可以在 nightly 版本的 traits 中使用。在 playground 上有一个完整的工作示例。我们将介绍一些限制,还有一些已知有待解决的错误,但我们认为它已经准备好供一些用户尝试。使用示例如下:#![feature(async_fn_in_trait)] trait Database { ...

Rust语言学习交流 547

LaTeX伪代码字体大小调整全攻略:从algorithm到algorithm2e的实战技巧

本文详细介绍了在LaTeX中调整伪代码字体大小的全面技巧,涵盖algorithm、algorithmicx和algorithm2e等主流算法包的使用方法。从基础命令到高级配置,帮助用户解决学术论文和技术文档中的伪代码排版问题,提升算法展示的专业性和可读性。特别针对不同期刊格式要求,提供了实用的字体调整方案。

weixin_29297127的博客 437

Win11下DCMTK的Visual Studio 2022配置教程

最近有个新需求,需要完成读取和解析DICOM数据相关的SDK

qq_45797625的博客 3262

【Rust日报】2023-10-16 为什么要异步 Rust

为什么要异步 Rust这篇文章讨论了Rust的async/await语法设计。作者指出,async/await语法最初发布时备受瞩目和期待。但最近,接受程度有些参差不齐。作者认为,随着时间的推移,反对者变得更加众多,他们的语气也变得更加强硬。在某种程度上,这只是炒作周期自然进展的结果,但作者也认为随着我们与原始设计过程越来越远,一些背景已经丢失了。作者认为 Rust 中异步编程是一项强大的功...

Rust语言学习交流 278

芒果YOLO11改进106:检测头篇DynamicHead动态检测头:基于DynamicHead,二次原创提出SATNeXt模块,提出全新的动态DynamicSatDetect

检测头篇DynamicHead动态检测头:基于DynamicHead,二次原创提出SATNeXt模块,提出全新的动态DynamicSatDetect

包括YOLOv5、YOLOv7、YOLOv8等模型改进 774

async-trait:异步 trait 的解决方案

`async-trait` 是一个 Rust 语言的开源项目,旨在解决 Rust 中异步 trait 的实现问题。该项目由 dtolnay 创建和维护,主要使用 Rust 编程语言。 ## 核心功能 `async-trait` 的核心功能是提供了类型擦除(type erasure)的机制,使得含有异步方法的 trait 能够作为 trait 对象(dyn Trait)使用。在 Rust 标准库...

gitblog_00421的博客 645

代谢组数据分析(二十二):代谢组原始数据Zscore标准化和主成分分析(PCA)及热图

本教程介绍了使用Z-score标准化和主成分分析(PCA)进行代谢组学数据预处理和分析的方法。代谢物原始数据具有高噪声、高维小样本、高变异性和复杂相互作用关系等特点,因此在数据分析时需注意数据质量控制、参数选择、批次效应、假阳性控制等问题。Z-score标准化通过将数据转换为均值为0、标准差为1的标准正态分布,提高了数据的可比性,减少了系统误差,并适应了统计分析方法的需求。PCA则通过降维、去相关和特征提取,帮助理解数据的结构和模式。教程还详细介绍了数据预处理步骤,包括填补缺失值、设置名称和顺序等,并提供了

专注生信领域 2864

基于深度学习的航拍图像中地物变化检测

随着无人机技术的飞速发展,航拍图像成为获取地表信息的重要手段之一。然而,由于地球表面的不断变化,监测和识别航拍图像中的地物变化成为一项具有挑战性的任务。在人工智能领域,深度学习技术的兴起为地物变化检测提供了全新的解决方案。本文将介绍基于深度学习的航拍图像中地物变化检测的方法,并附上相应的代码实例。

一键难忘的博客 4179

Rust中Sync Trait

在Rust中,基本数据类型(例如整数、浮点数等)和常见的标准库类型(例如Vec、Mutex、RwLock等)都实现了Sync trait。如果要自定义类型实现Sync trait,需要确保在实现过程中不会发生数据竞争。顺便一提:一般而言,在计算机编程中,Sync 是 Synchronous的缩写 意为“同步”,Async 是Asynchronous的缩写 意为“异步”。因此,对于需要在多线程中使用的类型,建议实现Sync trait来确保线程安全。具体而言,实现了Sync trait的类型可以。

mikisan555的博客 611

L3自动驾驶时代,域控制器核心解析(第一篇:架构演进与五域基础)

本文作为L3域控制器系列的第一篇,核心梳理了3个关键内容:L3时代为什么必须用域控制器:解决分布式ECU的协同、算力、迭代难题;域控制器的演进路线:从分布式ECU→域集中式→中央计算平台;博世五域架构的核心解析:五大域的定位、功能与典型方案。后续将持续更新系列文章,重点覆盖:第二篇:L3对域控制器的关键技术要求(传感器、算力、线控、软件、通信);第三篇:域控制器全产业链拆解(上游芯片、中游器件、下游总成);第四篇:域控制器的产业机遇与国产替代进展。

getusushu的博客 554

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

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

景天科技苑 2万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Rust异步编程进阶:Future手动实现、Stream与异步安全

本文深入探讨了Rust异步编程的高级主题,包括手动实现Future、处理异步数据流(Stream)以及异步安全等核心概念。文章首先讲解了手动实现Future的必要性和具体步骤,包括定义Future结构体、实现Future trait、处理自引用结构体(Pin)以及组合多个Future。然后介绍了异步Stream的概念和实现方法,强调其与Iterator的相似性但操作是异步的。这些高级技术对于调试异步代码、性能优化、实现自定义异步操作以及深入理解Rust异步原理都具有重要意义。

编程世界如山海,探索不止。 9206

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Rust 学习笔记:Future trait 和 Async 语法

Rust 学习笔记:Future trait 和 Async 语法

ProgramNovice的博客 1459

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

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 框架。方便软硬件开发者查阅学习。欢迎关注! 2224

Rust async/await 的状态机实现:从 Future Trait 到零开销异步的秘密

Rust 的 async/await 通过对编译器的深度利用,实现了异步编程中的"零成本抽象"。Future 状态机的每字节开销都由开发者显式控制,不做任何隐式的堆分配。I/O 密集型高并发服务:Rust async 的内存效率优势显著;CPU 密集型任务:使用 Rayon 等线程池模型,async 无额外收益;团队 Rust 经验不足:Go 的 Goroutine 模型的学习曲线远低于 Rust async。

iymei4986533030的博客 4592

Rust异步编程深度解析:从Future到Runtime

异步编程已经成为现代高性能应用开发的核心能力。无论是需要同时处理成千上万个连接的 Web 服务器,还是执行大量并发 I/O 操作的数据处理系统,异步模型都能显著提升系统的吞吐量与资源利用率。与 Go 语言的 goroutine 或 JavaScript 的 Promise 不同,Rust 的异步模型建立在零成本抽象的原则之上,通过编译期优化实现了既安全又高效的异步执行。 然而,这种设计也带来了一定的学习曲线 —— async/await 关键字背后隐藏着复杂的状态机转换,Future trait 的工作机制

Allen- 5665

RustTraining 异步训练指南:彻底理解 Rust Async Traits(RPITIT、trait_variant 与 async 闭包)

> 本指南基于 RustTraining 仓库的 Async Rust 训练书([async-book](https://link.gitcode.com/i/bb0f17dac42de72eeaef33b993afa3d0))第 10 章展开。它系统讲解 async 方法进入 trait 的完整演进历程:从 Rust 1.75 起原生支持的 RPITIT(Return Position Impl

gitblog_00685的博客 867

Rust 异步 Trait 详解:GAT、async-trait 库与 dyn Async

async fnasync fnin traits很难,因为它返回一个不透明的、带生命周期的类型。库:通过将async fn转换为返回的普通fn来解决此问题。易于使用,支持dyn Trait,但有堆分配和动态分发开销。GAT 模式:通过使用泛型关联类型()来精确定义返回的 Future 类型,实现零开销和**静态分发。Rust 1.75+:原生支持async fnin traits,编译器会自动使用 GAT 模式,这是未来的标准。

m0_46721576的博客 932

Rust异步编程之async与.await

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

飞鹰技术 1098

Rust 异步编程 async/await

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

QQ672168的博客 1344

异步编程: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

Future trait的定义与实现:Rust异步编程的基石

本文深入解析了Rust中Future trait的核心设计原理和实现机制。Future trait通过poll方法和Poll枚举实现非阻塞异步操作,结合Pin类型解决自引用安全问题,利用Waker实现高效唤醒机制。编译器将async/await转换为优化的状态机结构,实现零成本抽象。文章还展示了手动实现基础Future、定时器Future以及组合Future的具体代码,揭示了Rust异步编程的底层原理。这些设计使Rust能够高效处理大量并发任务,同时保持内存安全和性能优势。

超哥的博客 567

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万+
上一篇: Rust Async 异步编程(五):执行器和系统 I/O
下一篇: Rust Async 异步编程(三):使用 Waker 来唤醒任务
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、付费专栏及课程。

余额充值