Rust语言之多线程
文章目录
- 一、简介
- 二、创建线程
- 1.创建一个线程
- 2.创建多个线程
- 生成随机数
- 尝试让程序睡一会儿
- 引入多线程
- 三、线程返回值的处理
- 1.每个线程处理一个独立的值
- 2.多个线程处理一个值
- Arc(原子引用计数)
- Mutex(互斥锁)
- RwLock(读写锁)
一、简介
多线程 是一种并发执行的技术,它允许一个程序或进程同时执行多个线程。每个线程都是程序执行的一个独立路径,它们可以并行运行,共享进程的资源(如内存空间),但每个线程有自己的指令指针、堆栈和局部变量。多线程的主要目的是提高程序的执行效率,通过同时执行多个任务来充分利用计算机的多核处理器。
- 在Rust语言中,多线程编程是通过标准库中的
std::thread
模块来实现的。Rust提供了创建和管理线程的API,以及用于线程间同步和通信的机制,如互斥锁(Mutex)和通道(Channel)。
二、创建线程
1.创建一个线程
use std::thread; fn main() { // 创建一个新线程 let handle = thread::spawn(|| { // 在新线程中执行的代码 println!("Hello from a new thread!"); }); // 等待线程结束 handle.join().unwrap();
}
2.创建多个线程
use std::thread; fn main() { // 创建一个向量来存储线程的句柄 let mut threads = vec![]; // 创建多个线程 for i in 0..5 { // 使用闭包捕获变量i的值 let thread_number = i; let handle = thread::spawn(move || { // 在新线程中打印线程编号 println!("线程 {} 正在运行", thread_number); }); // 将线程句柄添加到向量中 threads.push(handle); } // 等待所有线程完成 for handle in threads { handle.join().unwrap(); } // 在主线程中打印一些信息 for i in 0..5 { println!("主线程打印数字: {}", i); }
}# 输出结果:
线程 0 正在运行
线程 1 正在运行
线程 2 正在运行
线程 3 正在运行
线程 4 正在运行
主线程打印数字: 0
主线程打印数字: 1
主线程打印数字: 2
主线程打印数字: 3
主线程打印数字: 4
- 从输出结果上看,仍然像是顺序执行,所以这里引入一个休眠,让线程执行的时候随机休眠0-3秒。
生成随机数
由于Rust核心语言中没有随机数生成的函数,需要使用rand库来进行
# 首先需要在Cargo.toml中添加以下内容
[dependencies]
rand = "0.8"# 然后在代码中用use 引入
use rand::Rng;
use rand::thread_rng; fn main() { let mut rng = thread_rng(); for _i in 0..10{let random_number = rng.gen_range(1..4); println!("随机数是: {}", random_number); }
}
# 结果:
随机数是: 3
随机数是: 1
随机数是: 3
随机数是: 1
随机数是: 3
随机数是: 1
随机数是: 3
随机数是: 2
随机数是: 3
随机数是: 1
随机数生成的区间与循环一样,是一个前闭后开的区间
尝试让程序睡一会儿
use rand::Rng;
use std::{thread::sleep, time::Duration}; fn main() { // 创建一个随机数生成器 let mut rng = rand::thread_rng(); // 生成一个0到3之间的随机秒数 let random_seconds: u64 = rng.gen_range(0..4); // 将秒数转换为Duration let duration = Duration::from_secs(random_seconds); // 让当前线程睡眠指定的时间 sleep(duration); // 之后的代码会在等待后执行 println!("等待了 {} 秒", random_seconds);
}
引入多线程
use rand::Rng;
use std::{thread::sleep, time::Duration};
use std::thread; fn main() { // 创建一个向量来存储线程的句柄 let mut threads = vec![]; // 创建多个线程 for i in 1..=10 { // 使用闭包捕获变量i的值 let thread_number = i; let handle = thread::spawn(move || { // 在新线程中打印线程编号 println!("线程 {} 正在运行", thread_number); // 创建一个随机数生成器 let mut rng = rand::thread_rng(); // 生成一个0到3之间的随机秒数 let random_seconds: u64 = rng.gen_range(0..4); // 将秒数转换为Duration let duration = Duration::from_secs(random_seconds); // 让当前线程睡眠指定的时间 sleep(duration); println!("线程 {} 运行结束,休息了{}秒.", thread_number,random_seconds); }); // 将线程句柄添加到向量中 threads.push(handle); } // 等待所有线程完成 for handle in threads { handle.join().unwrap(); } // 在主线程中打印一些信息 for i in 0..5 { println!("主线程打印数字: {}", i); }
}# 输出结果
线程 3 正在运行
线程 2 正在运行
线程 5 正在运行
线程 7 正在运行
线程 7 运行结束,休息了0秒.
线程 4 正在运行
线程 6 正在运行
线程 1 正在运行
线程 8 正在运行
线程 9 正在运行
线程 10 正在运行
线程 6 运行结束,休息了1秒.
线程 4 运行结束,休息了1秒.
线程 3 运行结束,休息了1秒.
线程 9 运行结束,休息了1秒.
线程 1 运行结束,休息了2秒.
线程 10 运行结束,休息了2秒.
线程 5 运行结束,休息了2秒.
线程 2 运行结束,休息了3秒.
线程 8 运行结束,休息了3秒.
主线程打印数字: 0
主线程打印数字: 1
主线程打印数字: 2
主线程打印数字: 3
主线程打印数字: 4
三、线程返回值的处理
对于有返回值的多线程来说有两种情况,一种是每个线程处理一个独立的值,用向量接收,另一种是多个线程处理一个值。
1.每个线程处理一个独立的值
use std::thread; fn main() { let mut handles = vec![]; for i in 0..5 { let handle = thread::spawn(move || { return i*i;}); handles.push(handle); } let mut results = vec![]; for handle in handles { match handle.join() { Ok(value) => results.push(value), Err(e) => println!("Thread panicked: {:?}", e), } } println!("Results: {:?}", results); //Results: [0, 1, 4, 9, 16]
}
2.多个线程处理一个值
由于多个线程处理一个值,可能造成条件竞争,属于线程不安全行为,Rust语言中提供了3种处理行为。
- Arc 只读访问,用于共享只读数据,通过原子引用计数管理生命周期。
- Mutex 互斥锁,用于保护数据,确保一次只有一个线程可以访问数据(提供独占访问)。
- RwLock 读写锁,用于保护数据,但允许多个读者同时访问,写者必须独占访问。
Arc(原子引用计数)
Arc
是一个提供共享所有权的智能指针。它用于在多个所有者之间共享数据,且只允许对这些数据进行只读访问。Arc通过原子操作维护一个引用计数,确保数据的生命周期至少与最长的所有者一样长。当最后一个Arc指针被丢弃时,其指向的数据也会被释放。
use std::sync::Arc;
use std::thread; fn main() { // 创建一个要在多个线程之间共享的值 let data = Arc::new(vec![1, 2, 3, 4, 5]); // 创建一个向量来存储线程的句柄 let mut handles = vec![]; println!("Thread {:?} is reading value: {:?}", thread::current().id(), &data); // 主线程的线程ID为 1// 创建几个线程来只读访问数据 for _i in 0..data.len() { let data = data.clone(); // 克隆Arc以便在线程中使用 let handle = thread::spawn(move || { // 获取Vec的引用以便索引 // 使用 {:?} 来打印 ThreadId println!("Thread {:?} is reading value: {:?}", thread::current().id(), &data); }); handles.push(handle); } // 等待所有线程完成 for handle in handles { handle.join().unwrap(); }
}
# 运行结果
Thread ThreadId(1) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(2) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(3) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(4) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(5) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(6) is reading value: [1, 2, 3, 4, 5]
Mutex(互斥锁)
Mutex
是一个提供互斥访问的智能指针。它用于保护数据,确保一次只有一个线程能够访问数据。当一个线程拥有Mutex的锁时,其他尝试获取锁的线程将被阻塞,直到锁被释放。
use std::sync::{Arc, Mutex};
use std::thread; fn main() { // 创建一个Arc包裹的互斥锁和值 let counter = Arc::new(Mutex::new(1)); let mut handles = vec![]; // 创建几个线程来增加计数器 for i in 1..10 { // 克隆Arc智能指针,而不是Mutex或它的值 let counter = Arc::clone(&counter); let handle = thread::spawn(move || { // 获取互斥锁以便修改值 let mut num = counter.lock().unwrap(); *num *=i;}); handles.push(handle); } // 等待所有线程完成 for handle in handles { handle.join().unwrap(); } // 输出最终计数器的值 println!("Result: {}", *counter.lock().unwrap()); // Result: 362880
}
RwLock(读写锁)
RwLock
是一个提供读写锁定的智能指针。与Mutex不同,RwLock允许多个读者同时访问数据,但写者必须独占锁。当写者拥有锁时,任何尝试获取读锁或写锁的线程都将被阻塞。当没有写者时,可以有多个读者同时访问数据。
use std::sync::{Arc, RwLock}; fn main() { let data = Arc::new(RwLock::new(0)); let mut handles = vec![]; // 创建多个读线程 for i in 0..5 { let data = Arc::clone(&data); let handle = std::thread::spawn(move || { let num = data.read().unwrap(); println!("Thread {} Reading value: {}", i,*num); }); handles.push(handle); } // 创建一个写线程 let data = Arc::clone(&data); let handle = std::thread::spawn(move || { let mut num = data.write().unwrap(); *num += 1; println!("Writing value: {}", *num); }); handles.push(handle); for handle in handles { handle.join().unwrap(); }
}
# 第一次执行结果
Thread 0 Reading value: 0
Thread 3 Reading value: 0
Thread 1 Reading value: 0
Thread 4 Reading value: 0
Thread 2 Reading value: 0
Writing value: 1
# 第二次执行结果
Thread 0 Reading value: 0
Thread 3 Reading value: 0
Thread 1 Reading value: 0
Thread 4 Reading value: 0
Thread 2 Reading value: 0
Writing value: 1
这里有一个问题,就是如果写线程最后执行,那么读线程读的都是原始数据,如果写线程先执行,那么读的就是修改后的数据,所以对读写顺序有要求的话应该做好时序的控制
相关文章:
Rust语言之多线程
文章目录 一、简介二、创建线程1.创建一个线程2.创建多个线程生成随机数尝试让程序睡一会儿引入多线程 三、线程返回值的处理1.每个线程处理一个独立的值2.多个线程处理一个值Arc(原子引用计数)Mutex(互斥锁)RwLock(读…...
现有的通用模型中融入少量中文数据没有太大意义少量的数据就能影响整个大模型
相关链接:只修改一个关键参数,就会毁了整个百亿参数大模型? | 新程序员-CSDN博客 现象 1:mBERT 模型的跨语言迁移 现象 2:大语言模型同样存在显著的语言对齐 现象 3:知识与语言分离 现象 4:…...

vscode 开发代码片段插件
环境准备 node - 20v版本 ,推荐使用nvm进行版本控制全局安装 "yo" 是 Yeoman 工具的命令行工具, npm i yo -g全局安装 generator-code 是一个 Yeoman 脚手架 gernerator-code npm i gernerator-code -g全局安装 npm install -g vsce官方文档 …...
算法竞赛STL:array的使用方法
算法竞赛STL:array的使用方法 文章目录 算法竞赛STL:array的使用方法array array 容器描述: array是一种固定大小的容器,它包含指定数量的元素。每个元素都有一个非负整数索引,用于访问或修改它。 使用方法ÿ…...
MyBatis sql拦截器实现一个自动根据租户进行分表的方案
需求描述: 在一个多租户系统中,通过 MyBatis 实现动态数据表分离。具体来说,您希望通过 MyBatis 拦截器在执行 SQL 时自动将表名根据当前租户 ID (tenantId) 进行修改。这样,每个租户的数据就可以存储在专属于它们的表中…...

TiDB in 2023, 一次简单的回顾丨PingCAP 唐刘
2023 年已经过去,TiDB 经过了一年的迭代,又往前进步了一点点,我们非常自豪的看到,TiDB 正在不断地帮助我们的客户成功,包括但不限于: ○ 首个云原生、分布式、全栈国产化银行核心业务系统投产上线丨TiDB …...

debug - 只要在内存中有显示相关的数据, 就会被CE找到
文章目录 debug - 只要在内存中有显示相关的实际数据, 就会被CE找到概述笔记demo实现demo运行效果用CE查找实际数据地址找到自己的调试点 - 方法1找到自己的调试点 - 方法2打补丁备注END debug - 只要在内存中有显示相关的实际数据, 就会被CE找到 概述 自己写了一个demo, 想验…...

Redis 单个与多节点如何实现分布式锁
分布式锁 在许多环境中,分布式锁是非常有用的原语,在这些环境中,不同的进程必须以互斥的方式操作共享资源。在应对并发问题时,Redis 客户端还可以通过加锁的方式,来控制并发写操作对共享数据的修改,从而保…...

频段划分学习射频知识的意义
一、射频电路设计与低频电路设计的不同点 随着频率提高,相应电磁波的波长与变得可与分立电路元件的尺寸相比拟时,电阻、电容和电感这些元件的电响应,将偏离他们的理想频率特性。以 WIFI 2.4G 频段为例,当频率为 2437MHz࿰…...
Effective Objective-C 学习(四)
掌握GCD及操作队列的使用时机 在执行后台任务时,GCD 并不一定是最佳方式。还有一种技术叫做 NSOperationQueue,它虽然与 GCD 不同,但是却与之相关,开发者可以把操作以 NSOperation 子类的形式放在队列中,而这些操作也…...

欢迎来到IT时代----盘点曾经爆火全网的计算机电影
计算机专业必看的几部电影 计算机专业必看的几部电影,就像一场精彩的编程盛宴!《黑客帝国》让你穿越虚拟世界,感受高科技的魅力;《社交网络》揭示了互联网巨头的创业之路,《源代码》带你穿越时间解救世界,这…...

光芒绽放:妙用“GLAD原则”打造标准的数据可视化图表
光芒绽放:妙用“GLAD原则”打造标准的数据可视化图表 文章目录 光芒绽放:妙用“GLAD原则”打造标准的数据可视化图表前言一、可视化工具有哪些?二、那如何做出正确可视化图表 ?GLAD原则1.G原则2.L原则3.A原则4.D原则 三、总结最后…...
如何设计出用于喜欢的界面
要设计出用户喜欢的界面,你可以考虑以下几个方面: 用户研究:首先要了解用户的需求和偏好。你可以通过用户调研、用户访谈和数据分析来获取这些信息。了解用户的行为模式、喜好和痛点,有助于设计出更吸引人的界面。 直观的布局&am…...

第三篇【传奇开心果系列】Python的文本和语音相互转换库技术点案例示例:pyttsx3实现语音助手经典案例
传奇开心果短博文系列 系列短博文目录Python的文本和语音相互转换库技术点案例示例系列 短博文目录一、项目背景和目标二、雏形示例代码三、扩展思路介绍四、与其他库和API集成示例代码五、自定义语音示例代码六、多语言支持示例代码七、语音控制应用程序示例代码八、文本转语音…...
JS中数组的常用方法
concat() 连接两个或更多的数组,并返回结果。 let array1 [1, 2, 3]; let array2 [4, 5, 6]; let concatenatedArray array1.concat(array2); console.log(concatenatedArray); // [1, 2, 3, 4, 5, 6]join() 把数组的所有元素放入一个字符串。元素通过指定…...

最好用的论文检索网站
网站展示: 网站链接 sci-hub文献检索 用途: 可以用文章的DOI来检索并下载文章...

AI专题:AI巨轮滚滚向前
今天分享的是电子系列深度研究报告:《AI专题:AI巨轮滚滚向前》。 (报告出品方:方正证券) 报告共计:65页 来源:人工智能学派 Gemini 1.5 Pro 性能显著增强,长上下文理解取得突破 …...

SpringBoot常见问题
1 引言 Spring Boot是一个基于Spring框架的快速开发脚手架,它简化了Spring应用的初始化和搭建过程,提供了众多便利的功能和特性,比如自动配置、嵌入式Tomcat等,让开发人员可以更加专注于业务逻辑的实现。 Spring Boot还提供了…...

五种多目标优化算法(MOAHA、MOGWO、NSWOA、MOPSO、NSGA2)性能对比,包含6种评价指标,9个测试函数(提供MATLAB代码)
一、5种多目标优化算法简介 1.1MOAHA 1.2MOGWO 1.3NSWOA 1.4MOPSO 1.5NSGA2 二、5种多目标优化算法性能对比 为了测试5种算法的性能将其求解9个多目标测试函数(zdt1、zdt2 、zdt3、 zdt4、 zdt6 、Schaffer、 Kursawe 、Viennet2、 Viennet3)ÿ…...

用 LangChain 和 Milvus 从零搭建 LLM 应用
如何从零搭建一个 LLM 应用?不妨试试 LangChain Milvus 的组合拳。 作为开发 LLM 应用的框架,LangChain 内部不仅包含诸多模块,而且支持外部集成;Milvus 同样可以支持诸多 LLM 集成,二者结合除了可以轻松搭建一个 LL…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...