用Tokio掌握Rust异步编程

在Rust中构建可伸缩且高效的应用程序时,异步编程必不可少。异步编程能显著提高性能,让代码在不阻塞的情况下并发处理多个任务。在本教程中,我们将探索Tokio,介绍异步编程原理及应用场景,并逐步带你编写异步代码。

Tokio 简介

Tokio 基于异步 I/O 模型。在传统的同步 I/O 中,当一个操作(如读取文件或进行网络请求)被发起时,程序会阻塞直到该操作完成。而 Tokio 的异步 I/O 允许程序在等待 I/O 操作完成的同时执行其他任务。异步编程主要应用有:web服务器、分布式数据库和网络服务等。

Tokio在Rust中启用了async/await语法,这使得异步代码像同步代码一样易于编写和读取。它被设计为有效地处理网络和I/ o绑定操作,这就是为什么它非常适合Rust的原因。
在这里插入图片描述

  • 异步 vs 同步

同步编程:在同步编程中,每个任务按顺序运行。例如,如果你有两个任务,任务1会在任务2开始之前完成。这可能会导致性能瓶颈,因为它在等待一个任务完成时阻塞了其他任务。

异步编程:使用异步编程,任务是并发执行的。而不是等待任务1完成,任务2可以开始,提高整体系统效率。Rust的Tokio通过允许任务在不阻塞整个程序的情况下运行来实现这一点。

在现实世界中,可以把它想象成做饭:在等待水烧开(一个I/O操作)的同时,您可以开始切菜(另一个任务),使整个过程更快。

第一个异步程序

让我们深入研究一些代码!下面是一个使用Tokio的async函数的简单示例。

use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    println!("Hello, Tokio!");

    let task_one = tokio::spawn(async {
        println!("Task one is started");
        sleep(Duration::from_secs(2)).await;
        println!("Task one is done");
    });

    let task_two = tokio::spawn(async {
        println!("Task two is started");
        sleep(Duration::from_secs(1)).await;
        println!("Task two is done");
    });

    // Await the tasks to complete
    task_one.await.unwrap();
    task_two.await.unwrap();
}

在这段代码中使用#[tokio::main]定义了一个异步main函数。在内部,我们生成两个任务:task_one和task_two。每个任务都模拟需要睡眠时间的操作。注意,任务是并发运行的——两个任务都开始执行,而不等待对方完成。这是使用Tokio进行异步编程的核心好处之一。输出结果如下:

Hello, Tokio!
Task one is started
Task two is started
Task two is done
Task one is done

我们看到的,task_two在task_one之前完成,这表明它们是异步执行的,不会互相阻塞。

Tokio核心概念

  • task Spawning(任务生成)

    在 Tokio 中,任务生成是指创建一个新的异步任务并将其提交到 Tokio 运行时的过程。任务是 Tokio 中异步执行的基本单元,类似于线程,但更加轻量级。通过生成任务,可以并发地执行多个异步操作,从而充分利用系统资源,提高应用程序的并发处理能力。

    示例代码:

    use tokio::task;
    async fn my_async_task() {
        println!("This is a new async task");
    }
    #[tokio::main]
    async fn main() {
        let handle = task::spawn(my_async_task());
        // 可以在这里继续执行其他操作
        handle.await;
    }
    
  • Task Joining(任务合并 / 等待)

    任务合并是指等待一个或多个之前生成的异步任务完成的操作。在并发编程中,当生成多个任务后,通常需要在某个时刻等待这些任务全部完成,以确保程序的正确性或者收集任务的执行结果。上面示例中task_one.await.unwrap() 语句阻塞主函数指导子任务执行完成。

    示例:

    use tokio::task;
    async fn task1() {
        println!("Task 1 is running");
    }
    async fn task2() {
        println!("Task 2 is running");
    }
    #[tokio::main]
    async fn main() {
        let handle1 = task::spawn(task1());
        let handle2 = task::spawn(task2());
        tokio::try_join!(handle1, handle2).unwrap();
        println!("Both tasks are completed");
    }
    
  • Timeouts(超时)

    超时是一种机制,用于在异步任务执行时间超过预期的情况下进行处理。在实际应用中,可能会遇到一些情况,例如网络请求因为网络故障或者服务器过载等原因迟迟没有响应。通过设置超时,可以避免程序无限期地等待,提高程序的可靠性和响应性。Tokio可以使用tokio::time::timeout很容易实现超时功能。

    示例:

    use tokio::time::{timeout, Duration};
    async fn slow_task() {
        // 模拟一个耗时任务
        tokio::time::sleep(Duration::from_secs(5)).await;
    }
    #[tokio::main]
    async fn main() {
        let result = timeout(Duration::from_secs(3), slow_task()).await;
        match result {
            Ok(_) => println!("Task completed within timeout"),
            Err(_) => println!("Task timed out"),
        }
    }
    

    在这个例子中,定义了slow_task函数,它通过sleep函数模拟了耗时 5 秒的任务。然后,在main函数中,使用timeout函数设置了 3 秒的超时时间来执行slow_tasktimeout函数返回Future,当await这个Future时,如果slow_task在 3 秒内完成,就会返回Ok结果;如果超过 3 秒还没有完成,就会返回Err结果,表示任务超时。根据返回的结果,在main函数中进行相应的打印输出。

异步编程场景

  • Web服务器:Tokio支持许多Web框架,如Actix和Warp。这些框架可同时处理数千个连接,因此非常适合构建高性能服务。
  • 微服务:如果你正在使用微服务架构,Tokio的异步运行时非常适合处理通过HTTP或WebSockets进行的服务间通信。
  • 网络客户端:Tokio可用于构建高效的网络客户端和服务端,实现以最小的开销处理数百万并发网络连接服务。

这些示例说明Tokio能在多个场景中实现异步应用,无论是构建api、微服务,还是游戏服务器,Tokio都将通过异步并发来增强应用系统的性能。

实战示例

对于异步I/O操作,使用Tokio实现任务并发运行,允许其他任务在I/O操作进行时继续执行。这样可以更好地利用系统资源,并提高应用程序的响应性。下面是Rust中异步读取文件的简化示例:

use tokio::fs::File;
use tokio::io::AsyncReadExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Asynchronously read a file
    let file_content = read_file_content("example.txt").await?;

    // Print "Hello, World!" along with the file content
    println!("Hello, World!");
    println!("File Content: {}", file_content);

    Ok(())
}

async fn read_file_content(file_path: &str) -> Result<String, Box<dyn std::error::Error>> {
    // Asynchronously open the file
    let mut file = File::open(file_path).await?;

    // Read the file content into a String
    let mut file_content = String::new();
    file.read_to_string(&mut file_content).await?;

    Ok(file_content)
}

async fn: 这意味着函数可以同时做多件事,而不必等待每件事完成。这就像一边写代码一边玩电子游戏。

**async fn read_file_content(file_path: &str) -> Result<String, Box<dyn std::error::Error>>** ,这是一个特殊的函数,它可以在你做其他事情的时候读取文件的内容,它会通知你,如果一切顺利,或者如果有问题,哪里出了问题。它就像一个聪明的助手,可以处理好和坏的情况。异步编程和‘ Result ’类型的结合是Rust的优雅特性之一,特别是在处理可能容易出错的异步操作时。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

推荐一款3D建模软件:Agisoft Metashape Pro

Agisoft Metashape Pro是一款强大的多视点三维建模设计辅助软件&#xff0c;Agisoft Metashape是一款独立的软件产品&#xff0c;可对数字图像进行摄影测量处理&#xff0c;并生成3D空间数据&#xff0c;用于GIS应用&#xff0c;文化遗产文档和视觉效果制作&#xff0c;以及间接…

记录日志中logback和log4j2不能共存的问题

本文章记录设置两个日志时候&#xff0c;控制台直接报错 标黄处就是错误原因&#xff1a;1. SLF4J(W)&#xff1a;类路径包含多个SLF4J提供程序。 SLF4J(W)&#xff1a;找到提供程序[org.apache.logging.slf4j. net]。 SLF4J(W)&#xff1a;找到提供程序[ch.qos.log .classi…

【论文阅读】Virtual Compiler Is All You Need For Assembly Code Search

阅读笔记:Virtual Compiler Is All You Need For Assembly Code Search 1. 研究背景 逆向工程:逆向工程需要在庞大的二进制文件中快速定位特定功能(例如恶意行为)。传统方法依赖于经验和启发式算法,效率低下。汇编代码搜索:通过自然语言搜索汇编代码功能,能够更高效地处…

洛古---越狱问题【快速幂】

今天和大家讲一个洛古的算法题&#xff0c;我觉得还是比较有含金量的&#xff0c;今天给大家分享一下 题目描述 监狱有 &#x1d45b;n个房间&#xff0c;每个房间关押一个犯人&#xff0c;有 &#x1d45a; 种宗教&#xff0c;每个犯人会信仰其中一种。如果相邻房间的犯人的宗…

Python3.11.9+selenium,选择证书用多线程+键盘enter解决

Python3.11.9+selenium,选择证书用多线程+键盘enter解决 1、遇到问题:弹出证书选择,无法点击确定 import pyautogui pyautogui.press(enter) 键盘enter也无法点击 2、解决办法:用多线程解决同时执行click链接和Enter点击证书的确定 1、点击操作 # # 通过文本链接文本…

1、使用vscode+eide+stm32cubeMx开发stm32

步骤1&#xff1a;在vscode中安装如下的插件 步骤2&#xff1a;点击Embedded IDE&#xff0c;点击“新建项目”-----空项目-----Cortex-M项目。 步骤3&#xff1a;输入项目名&#xff0c;回车后会要制定保存路径&#xff0c;此时就是一个已项目名命名的文件夹。 步骤4&#xff…

网站小程序app怎么查有没有备案?

网站小程序app怎么查有没有备案&#xff1f;只需要官方一个网址就可以&#xff0c;工信部备案查询官网地址有且只有一个&#xff0c;百度搜索 "ICP备案查询" 找到官方gov.cn网站即可查询&#xff01; 注&#xff1a;网站小程序app备案查询&#xff0c;可通过输入单位…

SpringCloud篇(注册中心 - Nacos)

目录 一、Nacos安装指南 1. Windows安装 1.1. 下载安装包 1.2. 解压 1.3. 端口配置 1.4. 启动 1.5. 访问 2. Linux安装 2.1. 安装JDK 2.2. 上传安装包 2.3. 解压 2.4. 端口配置 2.5. 启动 3. Nacos的依赖 二、Nacos注册中心的入门使用 1. 认识和安装Nacos 2. 服…

不对称信息

你买了一辆二手车&#xff0c;你并不知道它出过几次事故&#xff0c;但它之前的车主却对此了如指掌。来买保险的公司都是那些出险概率很大的&#xff08;比如矿工、化工厂&#xff09;&#xff0c;但那些安全的公司很少去买保险&#xff0c;这两种问题都属于信息不对称问题。 …

加深深度学习矩阵计算理解--用人类直觉 走进线性代数(非应试)

文章目录 前言一、向量二、线性组合、空间与基三、矩阵和线性变换四、矩阵乘法与线性变化复合1、矩阵乘法代表线性变换的复合2、实例说明 五、三维空间的线性变换1、基本性质2、直觉理解3、矩阵表示 六、行列式一、行列式的定义2、行列式在空间中的抽象理解 七、逆矩阵 列空间秩…

Collections 工具类

在 Java 编程中&#xff0c;集合&#xff08;Collections&#xff09;是处理数据的核心工具之一。为了简化集合操作并提高代码的可读性和可维护性&#xff0c;JDK 提供了一个强大的工具类&#xff1a;java.util.Collections。这个类包含了一系列静态方法&#xff0c;用于对集合…

Nginx在Windows上和Linux上(Docker启动)分别配置基本身份认证示例

场景 Nginx代理的资源或网站等&#xff0c;url直接暴露有风险&#xff0c;需要添加身份认证&#xff0c;即输入用户名密码后才能成功访问。 注&#xff1a; 博客&#xff1a;霸道流氓气质-CSDN博客 实现 Windows上配置Nginx实现基本身份认证 修改nginx的配置文件 添加基…

K8S之Prometheus 部署(二十)

部署方式&#xff1a;https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/prometheus 源码目录&#xff1a;kubernetes/cluster/addons/prometheus 服务发现&#xff1a;https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kube…

Spring Boot——日志介绍和配置

1. 日志的介绍 在前面的学习中&#xff0c;控制台上打印出来的一大堆内容就是日志&#xff0c;可以帮助我们发现问题&#xff0c;分析问题&#xff0c;定位问题&#xff0c;除此之外&#xff0c;日志还可以进行系统的监控&#xff0c;数据采集等 2. 日志的使用 在程序中获取日…

systemd

文章目录 运行模式获取需要开机启动的服务UnitServiceInstall 添加开机自启程序 在centos6之前使用上面方式&#xff08;串&#xff09; 在centos7之后(含centos7)使用systemd来管理程序, 通过ls -al /sbin/init 查看链接指向了systemd程序&#xff1a;&#xff08;并&#xf…

LeetCode 热题100之技巧关卡

1.只出现一次的数字 思路分析1&#xff1a;使用哈希表存储每个数字和该数字出现的次数。遍历数组即可得到每个数字出现的次数&#xff0c;并更新哈希表&#xff0c;最后遍历哈希表&#xff0c;得到只出现一次的数字。 具体实现代码&#xff08;详解版&#xff09;&#xff1a;…

如何优化Kafka消费者的性能

要优化 Kafka 消费者性能&#xff0c;你可以考虑以下策略&#xff1a; 并行消费&#xff1a;通过增加消费者组中的消费者数量来并行处理更多的消息&#xff0c;从而提升消费速度。 批量消费&#xff1a;配置 fetch.min.bytes 和 fetch.max.wait.ms 参数来控制批量消费的大小和…

服务器数据恢复——Ext4文件系统使用fsck后mount不上的数据恢复案例

关于Ext4文件系统的几个概念&#xff1a; 块组&#xff1a;Ext4文件系统的全部空间被划分为若干个块组&#xff0c;每个块组结构基本上相同。 块组描述符表&#xff1a;每个块组都对应一个块组描述符&#xff0c;这些块组描述符统一放在文件系统的前部&#xff0c;称为块组描述…

GIC寄存器介绍

往期内容 本专栏往期内容&#xff0c;interrtupr子系统&#xff1a; 深入解析Linux内核中断管理&#xff1a;从IRQ描述符到irq domain的设计与实现Linux内核中IRQ Domain的结构、操作及映射机制详解中断描述符irq_desc成员详解Linux 内核中断描述符 (irq_desc) 的初始化与动态分…

并发基础:(淘宝笔试题)三个线程分别打印 A,B,C,要求这三个线程一起运行,打印 n 次,输出形如“ABCABCABC....”的字符串【举一反三】

🚀 博主介绍:大家好,我是无休居士!一枚任职于一线Top3互联网大厂的Java开发工程师! 🚀 🌟 在这里,你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人,我不仅热衷于探索一些框架源码和算法技巧奥秘,还乐于分享这些宝贵的知识和经验。 💡 无论你是刚刚踏…