当前位置: 首页 > news >正文

Rust线程模型与线程创建

【赠书活动第4期】《Rust编程与项目实战》-CSDN博客

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

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;

模块std::thread

 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】  创建一个线程

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

(2)   在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,但并不保证其他线程会执行。这取决于操作系统如何调度线程。这个实例的输出结果是随机的,主线程一旦执行完成,程序就会自动退出,不会继续等待子线程。这就是子线程的输出结果为什么不全的原因。

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

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({// 子线程执行的代码});
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【高阶数据结构】图
  • 美团笔试-测试方向
  • 理解Tomcat的IP绑定与访问控制
  • C语言小练习(伍)
  • 【问卷表单系统】TDuckX-8月更新速览!
  • 嵌入式面经篇十——驱动开发
  • SAP Memory ABAP Memory超级详细解析
  • R 语言学习教程,从入门到精通,R CSV 文件使用(17)
  • 【axios get请求 中文乱码】
  • 运维工具之veyon安装和使用
  • 基于node.js的宠物寄存管理系统,基于express的宠物寄存系统
  • redis 主从复制方案
  • ThreadLoad如何防止内存溢出
  • 【BUU】[NewStarCTF 2023 公开赛道]Final -CP读取文件内容
  • 【C++】实现日期类相关接口
  • (三)从jvm层面了解线程的启动和停止
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • python学习笔记 - ThreadLocal
  • Redis的resp协议
  • Redis学习笔记 - pipline(流水线、管道)
  • vue2.0项目引入element-ui
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 巧用 TypeScript (一)
  • 如何进阶一名有竞争力的程序员?
  • 深入浅出Node.js
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 小程序 setData 学问多
  • 移动端高清、多屏适配方案
  • ​Java基础复习笔记 第16章:网络编程
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • #include
  • $.ajax中的eval及dataType
  • (11)MSP430F5529 定时器B
  • (a /b)*c的值
  • (AngularJS)Angular 控制器之间通信初探
  • (Python第六天)文件处理
  • (笔试题)分解质因式
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (二)测试工具
  • (三)uboot源码分析
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (转)Windows2003安全设置/维护
  • (转载)hibernate缓存
  • .bashrc在哪里,alias妙用
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net反混淆脱壳工具de4dot的使用
  • .NET是什么
  • .net中应用SQL缓存(实例使用)
  • /var/spool/postfix/maildrop 下有大量文件
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)