Rust多线程编程概述

news2024/11/17 3:40:04

【图书介绍】《Rust编程与项目实战》-CSDN博客

《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

Rust到底值不值得学,之一  -CSDN博客

Rust到底值不值得学,之二-CSDN博客

12.2 多线程编程概述

12.2.1  线程的基本概念

线程(Thread)是程序中独立运行的一部分。线程是进程(Process)中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。在有操作系统的环境中,进程往往被交替地调度得以执行,线程则在进程内由程序进行调度。由于线程并发很有可能出现并行的情况,因此在并行中可能遇到的死锁、延宕错误常出现于含有并发机制的程序中。

为了解决这些问题,很多语言(如Java、C#)采用特殊的运行时(Runtime)软件来协调资源,但这样无疑极大地降低了程序的执行效率。C/C++语言在操作系统的最底层也支持多线程,且语言本身及其编译器不具备侦察和避免并行错误的能力,这对于开发者来说压力很大,开发者需要花费大量的精力避免发生错误。

Rust不依靠运行时环境,这一点像C/C++一样。但Rust在语言本身就设计了包括所有权机制在内的手段来尽可能地把最常见的错误消灭在编译阶段,这一点其他语言不具备。但这不意味着我们编程的时候可以不小心,迄今为止由于并发造成的问题还没有在公共范围内得到完全解决,仍有可能出现错误,并发编程时要尽量小心!

12.2.2  并发

我们知道,如今CPU的计算能力已经非常强大,其速度比内存要高出许多个数量级。为了充分利用CPU资源,多数编程语言都提供了并发编程的能力,Rust也不例外。

聊到并发,就离不开多进程和多线程这两个概念。其中,进程是资源分配的最小单位,而线程是程序运行的最小单位。线程必须依托于进程,多个线程之间是共享进程的内存空间的。进程间的切换复杂、CPU利用率低等缺点让我们在做并发编程时更加倾向于使用多线程的方式。

当然,多线程也有缺点。其一是程序运行顺序不能确定,因为这是由内核来控制的;其二就是多线程编程对开发者要求比较高,如果不充分了解多线程机制的话,写出的程序就非常容易出Bug。

多线程编程的主要难点在于如何保证线程安全。什么是线程安全呢?因为多个线程之间是共享内存空间的,因此存在同时对相同的内存进行写操作,进而出现写入数据互相覆盖的问题。如果多个线程对内存只有读操作,没有任何写操作,那么也就不会存在安全问题,我们可以称之为线程安全。

常见的并发安全问题有竞态条件和数据竞争两种,竞态条件是指多个线程对相同的内存区域(称之为临界区)进行了“读取−修改−写入”这样的操作。而数据竞争则是指一个线程写一个变量,而另一个线程需要读这个变量,此时两者就是数据竞争的关系。这么说可能不太容易理解,不过不要紧,稍后将举两个具体的例子帮助大家理解。不过在此之前,先介绍一下在Rust中是如何进行并发编程的。

在Rust标准库中提供了两个模块来进行多线程编程:

(1)std::thread定义一些管理线程的函数和一些底层同步原语。

(2)std::sync定义了锁、Channel、条件变量和屏障。

12.2.3  Rust线程模型

一个正在执行的Rust程序由一组本地操作系统线程组成,每个线程都有自己的堆栈和本地状态。线程可以被命名,并为低级别同步提供一些内置支持。

线程之间的通信可以通过通道、Rust的消息传递类型以及其他形式的线程同步和共享内存数据结构来完成。特别是,保证线程安全的类型可以使用原子引用计数容器Arc在线程之间轻松共享。

Rust中的致命逻辑错误会导致线程死机(也称崩溃),在此期间,线程将展开堆栈,运行析构函数并释放所拥有的资源。Rust中的线程死机可以用catch_unwnd捕获(除非使用panic=abort编译)并从中恢复,或者用resume_unwnd恢复。如果没有捕捉到死机,线程将退出,但可以选择从具有连接的其他线程检测到死机。如果主线程死机而没有捕获到死机,则应用程序将使用非零的退出代码退出。

当Rust程序的主线程终止时,即使其他线程仍在运行,整个程序也会关闭。然而,该模块为自动等待线程的终止(即加入)提供了方便的设施。

Rust 中的多线程通过 std::thread模块来实现,它提供了创建和管理线程的功能。Rust 的多线程模型采用“共享状态,可变状态”(Shared State,Mutable State)的方式,这意味着多个线程可以访问同一个数据,但需要通过锁(Lock)来保证数据的安全性。

注意,要使用Rust标准库中的线程函数,通常要在文件开头包含std::thread,比如:

use std::thread;

12.3  模块std::thread

12.3.1  spawn创建线程

在Rust中,我们可以使用std::thread::spawn函数来创建一个新的线程,也称派生线程。该函数声明如下:

pub fn spawn<F, T>(f: F) -> JoinHandle<T>

F: FnOnce() -> T + Send + 'static,

T: Send + 'static,

参数f是一个闭包(Closure),是线程要执行的代码。spawn函数生成一个新线程,并返回JoinHandle(连接句柄),连接句柄提供了一个join方法,可用于连接派生的线程。如果派生的线程崩溃,join将返回一个错误信息。

如果删除连接句柄(JoinHandle),则派生的线程将隐式分离。在这种情况下,派生的线程可能不再连接。注意:程序员有责任最终连接它创建的线程或分离它们,否则将导致资源泄露。

正如用户在spawn的声明中所看到的,对spawn的闭包及其返回值都有两个约束,让我们来解释它们:

(1)静态约束意味着闭包及其返回值必须具有整个程序执行的生存期。这样做的原因是线程可以比创建它们的生存期更长。事实上,如果线程及其返回值可以比它们的调用程序更持久,我们需要确保它们在之后是有效的,因为我们不知道它们什么时候会返回,所以需要让它们尽可能长时间地有效,也就是说,直到程序结束,因此是“静态生存期”。

(2)Send约束是因为闭包需要按值从派生它的线程传递到新线程。它的返回值需要从新线程传递到连接它的线程。作为提醒,Send标记特性表示从一个线程传递到另一个线程是安全的。Sync表示在线程之间传递引用是安全的。

spawn函数的简单示例如下:

use std::thread;



fn main() {

    let handle = thread::spawn(|| {

        //子线程执行的代码

    });

}

子进程也就是主线程的派生线程。其中的||表示闭包,该闭包中的代码将在子线程中执行。调用thread::spawn方法会返回一个句柄,该句柄拥有对线程的所有权。通过这个句柄我们可以管理线程的生命周期和操作线程。thread::spawn 函数接受一个闭包作为参数,闭包中的代码会在子线程中执行。创建的新线程是“分离的”,这意味着程序无法了解派生线程何时完成或终止。下面是一个简单的实例。

【例12.1】  创建一个线程

   打开VS Code,单击菜单Terminal→New Termanal,执行命令cargo new myrust来新建一个Rust工程,工程名是myrust。

   在main.rs中,添加代码如下:

use std::{ thread, time::Duration };  		//导入线程模块和时间模块

fn main() {
    thread::spawn(|| {   						//创建一个新线程
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}

上面代码调用thread::spawn函数创建了一个新的线程,并在该线程中通过一个for循环准备打印9条信息,并且每输出一条信息就调用sleep函数休眠1毫秒。而主线程中的main函数中将打印4条信息,也是每输出一条信息就调用sleep函数休眠1毫秒。我们调用thread::sleep函数强制线程休眠一段时间,这就允许不同的线程交替执行。但要注意的是,当主线程结束的时候,整个进程就结束了,此时派生线程也会结束,所以派生线程中的打印信息是不会全部输出完毕的。也就是说,虽然某个线程休眠时会自动让出CPU,但并不保证其他线程会执行。这取决于操作系统如何调度线程。这个实例的输出结果是随机的,主线程一旦执行完成,程序就会自动退出,不会继续等待子线程。这就是子线程的输出结果为什么不全的原因。

   保存文件并运行,运行结果如下:

hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 4 from the main thread!

可以看到,主线程可以全部输出完毕,而派生线程则没有执行完全部for循环,符合预期。从结果中能看出两件事:第一,两个线程是交替执行的,但是并没有严格的顺序;第二,当主线程结束时,它并没有等子线程运行完。

Thread也支持通过std::thread::Builder结构体进行创建,Builder提供了一些线程的配置项,如线程名字、线程优先级、栈大小等,比如:

use std::thread;

fn main() {
let handle = Builder::new()
    .name("my_thread".to_string())         //设置新线程的名称是my_thread
    .stack_size(1024 * 4)                  //设置新线程的堆栈大小是1024*4
    .spawn({
         // 子线程执行的代码
    });
}

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

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

相关文章

校园洗护新体验:一键尽享便捷小程序功能全解析

校园洗护新体验&#xff1a;一键尽享便捷小程序功能全解析 1. **用户端尊享**&#xff1a;无论是渴望上门取件的便捷&#xff0c;还是偏爱送货至店的从容&#xff0c;乃至寄存网点的灵活&#xff0c;一切由您指尖掌控&#xff0c;尽享个性化服务。 2. **取货员专业视角**&…

云计算国标发布 云轴科技ZStack参编

近日&#xff0c;国家市场监督管理总局、国家标准化管理委员会发布中华人民共和国国家标准公告&#xff08;2024年第17号&#xff09;。上海云轴信息科技有限公司&#xff08;简称云轴科技ZStack&#xff09;参与起草的国家标准GB/T 32399-2024《信息技术 云计算 参考架构》正式…

分类预测|基于CNN提取特征使用支持向量机作为分类器进行分类预测CNN-SVM(SVM自动寻优c,g超参数)CNN和CNN-SVM

分类预测|基于CNN提取特征使用支持向量机作为分类器进行分类预测CNN-SVM&#xff08;SVM自动寻优c&#xff0c;g超参数&#xff09;CNN和CNN-SVM 文章目录 一、基本原理1. 卷积神经网络&#xff08;CNN&#xff09;简介CNN的基本结构 2. 支持向量机&#xff08;SVM&#xff09;…

WPF-快速构建统计表、图表并认识相关框架

一、使用ScottPlot.Wpf 官网地址&#xff1a;https://scottplot.net/quickstart/wpf/ 1、添加NuGet包&#xff1a;ScottPlot.Wpf 2、XAML映射命名空间&#xff1a; xmlns:ScottPlot"clr-namespace:ScottPlot.WPF;assemblyScottPlot.WPF" 3、简单示例&#xff1a;…

当视频编辑完成后,要将视频进行导出,其中mp4是人们常用的视频格式, Camtasia如何导出工程文件

当视频编辑完成后&#xff0c;要将视频进行导出&#xff0c;其中mp4是人们常用的视频格式。如果视频编辑尚未完成&#xff0c;则可以将工程文件暂时保存在电脑中&#xff0c;以便下次打开使用。今天的文章将为大家介绍Camtasia关于保存的相关内容。接下来我将为大家介绍&#x…

数据结构:树与二叉树

1、树的基本概念 1.1树的定义 树是n个结点的有限集。 若n0&#xff0c;称为空树&#xff1b;若n>0称为非空树&#xff0c;非空树有且仅有一个称之为根的结点。 除根结点以外的其余结点可分成m个互不相交的有限集T1,T2,......Tm,每个有限集合本身又是一棵树&#xff0c;并…

CentOS 部署 RocketMQ 详细指南

1. RocketMQ 5.3.0 简介 什么是 RocketMQ&#xff1f; Apache RocketMQ 是一个分布式消息中间件&#xff0c;最初由阿里巴巴开发并开源。它基于发布-订阅&#xff08;Pub-Sub&#xff09;模式&#xff0c;具有高性能、低延迟和高可靠性等特点。RocketMQ 支持大规模消息流处理…

Win11怎么把C盘分成几个盘?

很多Windows11用户会发现&#xff0c;系统默认只给电脑分配了一个C盘&#xff0c;而C盘不仅是系统盘&#xff0c;还是软件的默认安装位置&#xff0c;并且个人数据也是保存在C盘。这种情况下&#xff0c;电脑使用时间久了会遇到一个常见问题&#xff1a;C盘空间不足或是需要将C…

elasticsearch文档Delete By Query API(一)

这里的查询需要使用和Search API&#xff08;后文会讲&#xff09;相同的方式来将查询条件作为query的值传递&#xff0c;当然也可以使用q关键字&#xff0c;例如如下请求&#xff1a; curl -X POST “localhost:9200/twitter/_delete_by_query?pretty&quser:kimchy” -H…

828华为云征文|华为云Flexus X实例docker部署srs6并调优,协议使用webrtc与rtmp

828华为云征文&#xff5c;华为云Flexus X实例docker部署srs6并调优&#xff0c;协议使用webrtc与rtmp 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务…

六西格玛项目:从定义问题到控制成果,全程无忧——张驰咨询

什么是六西格玛项目 六西格玛项目是一种旨在通过改善企业质量流程管理&#xff0c;以“零缺陷”的完美商业追求&#xff0c;带动质量大幅提高、成本大幅度降低&#xff0c;最终实现企业财务成效的提升与企业竞争力的突破的管理策略。它由摩托罗拉公司的工程师比尔史密斯于1986…

文心一眼 4.0Turbo 免费领取5天体验卡

注&#xff1a;这个是官方的活动&#xff0c;目的在于用户推广&#xff0c;任何账号点击连接都可以领取&#xff0c;被分享者领取5天&#xff0c;分享者获得3天&#xff0c;上限320天。 官方活动跳转地址如下&#xff1a;​​​​​​点击这里 跳转后登录&#xff0c;完成一次…

浅析前端数据埋点监控:用户行为与性能分析的桥梁

在数字化时代&#xff0c;数据是企业决策的重要依据。前端作为用户与产品交互的第一线&#xff0c;其数据埋点监控不仅能够收集用户行为数据&#xff0c;帮助产品团队洞察用户需求&#xff0c;优化用户体验&#xff0c;还能分析性能数据&#xff0c;确保产品运行的流畅性。 简单…

LLM常见问题(思维链变体部分)

1. 为什么需要思维树 Tree of Thoughts&#xff08;TOT&#xff09;&#xff1f; 对于需要探索或预判战略的复杂任务来说&#xff0c;传统或简单的提示技巧是不够的。ToT 维护着一棵思维树&#xff0c;思维由连贯的语言序列表示&#xff0c;这个序列就是解决问题的中间步骤。使…

【计算机组成原理】你敢相信5.8+0.9=6.1这个等式居然成立!!!详细解读进制数之间的相互转换

进位计数制及其相互转换 导读一、进位计数法1.1 基数1.2 位权 二、不同进制数之间的相互转化2.1 转化方法2.2 任意进制转化成十进制2.3 十进制转化成任意进制2.4 二进制、八进制以及十六进制之间的相互转换2.4.1 二进制与八进制以及十六进制之间的关系2.4.2 二进制数与八进制数…

【C++】C++中的关键字:const、mutable、auto、new....

七、C中的关键字&#xff1a;const、mutable、auto、new、 本部分打算是尽量多的罗列出C中的关键字和一些花式操作。先总结一下我们之前讲过的&#xff1a;private、public、protect: 参考【C】类、静态、枚举、重载、多态、继承、重写、虚函数_静态与多态:重写、重载、模板-C…

如何使用PTK一键安装opengaussdb 5.0

1、关于PTK工具 MogDB数据库是云和恩墨基于openGauss开源数据库打造&#xff0c;安稳易用的企业级关系型数据库。 PTK是云和恩墨出品的一款工具&#xff0c;帮助用户更便捷地部署管理MogDB数据库。 1.1 使用场景 开发人员快速启动多个本地 MogDB 环境用户通过 PTK 快速安装…

(2)Django生产环境数据库的切换以及环境配置python-dotenv方案

简介 本文分享下个人目前在用的&#xff0c;DRF项目开发环境、生产环境数据库切换的方案。没做过什么大项目&#xff0c;可能实现的不是那么优雅。 主要思路就是通过python-dotenv库&#xff0c;编辑.env环境配置文件区分开发环境和生产环境&#xff0c;再使用数据库路由&…

IP 协议详解

一、认识 IP 地址与网络层的职责 网络层是OSI七层模型中的第三层&#xff0c;也是TCP/IP四层模型中的网络接入层。在这一层&#xff0c;数据包被封装并加上IP层的头部信息&#xff0c;以便在网络之间传输。网络层的主要功能包括路由选择、分段与重组、拥塞控制以及IP地址管理等…

深度学习--机器学习相关(2)

1.适 应 性 矩 估 计 适应性矩估计(Adaptive Moment Estimation,Adam)是一种可以代替传统的梯度下降 (SGD 和 MBGD) 的优化算法。Adam算法结合了适应性梯度算法和均方根传播的优点。 Momentum 在学习机器学习时是很可能遇到的&#xff0c;是动量的意思。动量不是速度和学…