首页 > 编程语言 >Rust async 编程

Rust async 编程

时间:2023-05-26 13:12:52浏览次数:48  
标签:mut 编程 Future let Rust async future fn

Rust async 编程

Asynchronous Programming in Rust:https://rust-lang.github.io/async-book/

中文书名《Rust 异步编程指南》:https://github.com/rustlang-cn/async-book

Rust语言圣经(Rust Course):https://course.rs/advance/async/getting-started.html

一、Getting Started

1.1 为什么使用 async

为什么使用 async

  • Async 编程,是一种并发(concurrent)编程模型
    • 允许你在少数系统线程上运行大量的并发任务
    • 通过 async/await 语法,看起来和同步编程差不多

其它的并发模型

  • OS 线程
    • 无需改变任何编程模型,线程间同步困难,性能开销大
    • 线程池可以降低一些成本,但难以支撑大量 IO 绑定的工作
  • Event-driven 编程
    • 与回调函数一起用,可能高效
    • 非线性的控制流,数据流和错误传播难以追踪
  • 协程(Coroutines)
    • 类似线程,无需改变编程模型
    • 类似 async ,支持大量任务
    • 抽象掉了底层细节(这对系统编程、自定义运行时的实现很重要)
  • Actor 模型
    • 将所有并发计算划分为 actor , 消息通信易出错
    • 可以有效的实现 actor 模型,但许多实际问题没解决(例如流程控制、重试逻辑)

Rust 中的 async

  • Future 是惰性的
    • 只有poll时才能取得进展, 被丢弃的 future 就无法取得进展了
  • Async是零成本的
    • 使用async ,可以无需堆内存分配(heap allocation)和动态调度(dynamic dispatch),对性能大好,且允许在受限环境使用 async
  • 不提供内置运行时
    • 运行时由Rust 社区提供,例如 tokio
  • 单线程、多线程均支持
    • 这两者拥有各自的优缺点

Rust 中的 async 和线程(thread)

  • OS 线程:
    • 适用于少量任务,有内存和CPU开销,且线程生成和线程间切换非常昂贵
    • 线程池可以降低一些成本
    • 允许重用同步代码,代码无需大改,无需特定编程模型
    • 有些系统支持修改线程优先级
  • Async:
    • 显著降低内存和CPU开销
    • 同等条件下,支持比线程多几个数量级的任务(少数线程支撑大量任务)
    • 可执行文件大(需要生成状态机,每个可执行文件捆绑一个异步运行时)

Async 并不是比线程好,只是不同而已!

总结:

  • 有大量 IO 任务需要并发运行时,选 async 模型
  • 有部分 IO 任务需要并发运行时,选多线程,如果想要降低线程创建和销毁的开销,可以使用线程池
  • 有大量 CPU 密集任务需要并行运行时,例如并行计算,选多线程模型,且让线程数等于或者稍大于 CPU 核心数
  • 无所谓时,统一选多线程

例子

如果想并发的下载文件,你可以使用多线程如下实现:

fn get_two_sites() {
    // Spawn two threads to do work. 创建两个新线程执行任务
    let thread_one = thread::spawn(|| download("https://www.foo.com"));
    let thread_two = thread::spawn(|| download("https://www.bar.com"));

    // Wait for both threads to complete. 等待两个线程的完成
    thread_one.join().expect("thread one panicked");
    thread_two.join().expect("thread two panicked");
}

使用async的方式:

async fn get_two_sites_async() {
    // Create two different "futures" which, when run to completion, 创建两个不同的`future`,你可以把`future`理解为未来某个时刻会被执行的计划任务
    // will asynchronously download the webpages. 当两个`future`被同时执行后,它们将并发的去下载目标页面
    let future_one = download_async("https://www.foo.com");
    let future_two = download_async("https://www.bar.com");

    // Run both futures to completion at the same time. 同时运行两个`future`,直至完成
    join!(future_one, future_two);
}

自定义并发模型

  • 除了线程和async,还可以用其它的并发模型(例如 event-driven)

1.2 Rust Async 的目前状态

Async Rust 目前的状态

  • 部分稳定,部分仍在变化。
  • 特点:
    • 针对典型并发任务,性能出色
    • 与高级语言特性频繁交互(生命周期、pinning)
    • 同步和异步代码间、不同运行时的异步代码间存在兼容性约束
    • 由于不断进化,维护负担更重

语言和库的支持

  • 虽然Rust本身就支持Async编程,但很多应用依赖与社区的库:
    • 标准库提供了最基本的特性、类型和功能,例如 Future trait
    • async/await 语法直接被Rust编译器支持
    • futures crate 提供了许多实用类型、宏和函数。它们可以用于任何异步应用程序。
    • 异步代码、IO 和任务生成的执行由 "async runtimes" 提供,例如 Tokio 和 async-std。大多数async 应用程序和一些 async crate 都依赖于特定的运行时。

注意

  • Rust 不允许你在 trait 里声明 async 函数

编译和调试

  • 编译错误:
    • 由于 async 通常依赖于更复杂的语言功能,例如生命周期和Pinning,因此可能会更频繁地遇到这些类型的错误。
  • 运行时错误:
    • 每当运行时遇到异步函数,编译器会在后台生成一个状态机,Stack traces 里有其明细,以及运行时调用的函数。因此解释起来更复杂。
  • 新的失效模式:
    • 可能出现一些新的故障,它们可以通过编译,甚至单元测试。

兼容性考虑

  • async和同步代码不能总是自由组合

    • 例如,不能直接从同步函数来调用 async 异步函数
  • Async 代码间也不总是能自由组合

    • 一些crate依赖于特定的 async 运行时
  • 因此,尽早研究确定使用哪个 async 运行时

性能特征

  • async 的性能依赖于运行时的表现(通常较出色)

1.3 async/await 入门

async

  • async 把一段代码转化为一个实现了Future trait 的状态机
  • 虽然在同步方法中调用阻塞函数会阻塞整个线程,但阻塞的Future将放弃对线程的控制,从而允许其它Future来运行。
~/rust via 

标签:mut,编程,Future,let,Rust,async,future,fn
From: https://www.cnblogs.com/QiaoPengjun/p/17434443.html

相关文章

  • 【java】同步异步和多线程编程
    Java基本概念并发基于时间段内的,同时发生(处理多个任务的能力,时间段)存在同步和互斥的问题(任务之间的时序问题)同步:前一个处理的结果作为下一个处理的资源(互相之间有依赖)互斥:不能同时使用临界资源。解决时序问题的机制:锁,信号量,原子操作Java中的多线程机制并行(完全......
  • 异步编程(Thread、ThreadPool、Task、异步关键字async/await)
    一、什么是异步Thread,是微软.Net1.0推出;ThreadPool 是微软.Net2.0推出;Task是微软.Net4.0推出;async/await是微软.Net5.0推出;       同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法;当一个方......
  • UNIX网络编程:socket & select() 实现clients/server通信
    一、问题引入UNIX网络编程卷1:套接字联网API(第三版)第6章介绍了I/O复用可以通过select()的单进程服务器与多客户端通信。UNIX下可用的5中I/O模型:阻塞式I/O非阻塞式I/OI/O(select和poll)信号驱动式I/O(SIGIO)异步I/O(POSIX的aio_系列函数)其中前面4种可以分为同步I/O,第五种为......
  • PB编程,API函数和PB编程绝招
    1. 如何使PB窗口总在最上层 通过SetWindowPos函数吧窗口的显示层次修改为HWND_TOPMOST,就可以使指定窗口永远不会被其他窗口覆盖,该函数声明为: Function Long SetWindowPos(Long hwnd, Long  ord, Long x, Long y, Long dx, Long dy, Long uflag) Library ......
  • JS的异步化特征async await
    参考:https://segmentfault.com/a/1190000007535316https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promisehttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/awaithttps://developer.mozilla.org/zh-CN/......
  • 通过Java技术学习C++编程
    C++是一种广泛应用于系统级编程和高性能计算领域的强大编程语言。尽管Java和C++是两种不同的语言,但是通过Java技术的学习和应用,我们可以更好地理解C++的概念和编程技巧。本文将以Java为基础,展示一些C++代码示例,帮助读者掌握C++的基本语法和常用功能。HelloWorld程序让我们从经典......
  • Java编程核心之继承
    学习目标掌握继承的优点和实现掌握子类重写父类方法掌握继承下构造方法的过程掌握抽象类和抽象方法的使用前言:在写程序的过程中会有一些重复的代码,我们可以使用继承的方式把重复的代码提取到父类中,这样在子类中,就可以起到优化代码的作用。继承的定义继承是面向对象语法的三大特征之......
  • C语言编程—函数的介绍
    函数是一组一起执行一个任务的语句。每个C程序都至少有一个函数,即主函数main(),所有简单的程序都可以定义其他额外的函数。您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。函数声明告......
  • RustDesk,可私有部署的远程控制软件
    一、服务端:运行压缩包里面的RustDeskServer.Setup.exe安装即可  二、客户端:输入ID服务器IP地址即可开始连接,完全免费使用,无任何限制 ......
  • T-SQL编程
    @@t-sql循环  T-SQL编程  一、T-SQL编程变量声明、为变量赋值、输出变量必须赋初值,不然为null,与null计算所得的结果还是null  二、T-SQL中的while循环,无for循环begin......end相当于C#编程中while循环的大括号  三、T-SQL中if....else....  四、......