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

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是一种固定大小的容器,它包含指定数量的元素。每个元素都有一个非负整数索引,用于访问或修改它。 使用方法&#xff…...

MyBatis sql拦截器实现一个自动根据租户进行分表的方案

需求描述: 在一个多租户系统中,通过 MyBatis 实现动态数据表分离。具体来说,您希望通过 MyBatis 拦截器在执行 SQL 时自动将表名根据当前租户 ID (tenantId) 进行修改。这样,每个租户的数据就可以存储在专属于它们的表中&#xf…...

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&#xff0…...

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)&#xff…...

用 LangChain 和 Milvus 从零搭建 LLM 应用

如何从零搭建一个 LLM 应用?不妨试试 LangChain Milvus 的组合拳。 作为开发 LLM 应用的框架,LangChain 内部不仅包含诸多模块,而且支持外部集成;Milvus 同样可以支持诸多 LLM 集成,二者结合除了可以轻松搭建一个 LL…...

[Bug解决] Invalid bound statement (not found)出现原因和解决方法

1、问题描述 在写了一个很普通的查询语句之后,出现了下面的报错信息 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.xxx.oauth.mapper.WxVisitorQrBeanMapper.selectByComIdAndEmpId at org.apache.ibatis.binding.Mappe…...

Qt:Qt3个窗口类的区别、VS与QT项目转换

一、Qt3个窗口类的区别 QMainWindow:包含菜单栏、工具栏、状态栏 QWidget:普通的一个窗口,什么也不包括 QDialog:对话框,常用来做登录窗口、弹出窗口(例如设置页面) QDialog实现简易登录界面…...

uni-app判断不同端

大家好&#xff0c;今天给大家分享的知识是在uni-app中如何区分是在什么端操作的程序 话不多说直接上代码&#xff1a; // #ifdef APP-PLUS<view>APP端</view>// #endif// #ifdef H5<view>H5端</view>// #endif// #ifdef MP<view>小程序端</v…...

计算机网络-网络设备防火墙是什么?

一、防火墙基本概念 前面我们学习了交换机、路由器是网络中常用的设备&#xff0c;现实中还有一个很重要的设备-防火墙。防火墙这一设备通常用于两个网络之间有针对性的、逻辑意义上的隔离。在网络通信领域&#xff0c;防火墙是一种安全设备。它用于保护一个网络区域免受来自另…...

Code Composer Studio (CCS) - Breakpoint (断点)

Code Composer Studio [CCS] - Breakpoint [断点] 1. BreakpointReferences 1. Breakpoint 选中断点右键 -> Breakpoint Properties… Skip Count&#xff1a;跳过断点总数&#xff0c;在断点执行之前设置总数 Current Count&#xff1a;当前跳过断电累计值 References […...

人工智能_普通服务器CPU_安装清华开源人工智能AI大模型ChatGlm-6B_001---人工智能工作笔记0096

使用centos安装,注意安装之前,保证系统可以联网,然后执行yum update 先去更新一下系统,可以省掉很多麻烦 20240219_150031 这里我们使用centos系统吧,使用习惯了. ChatGlm首先需要一台个人计算机,或者服务器, 要的算力,训练最多,微调次之,推理需要算力最少 其实很多都支持C…...

分层钱包HD钱包

bc1 开头的通常指的是比特币&#xff08;Bitcoin&#xff09;的地址&#xff0c;这种格式遵循了比特币改进提案BIP 0173中定义的Bech32编码格式。Bech32地址也被称为"SegWit"地址&#xff0c;它们支持Segregated Witness功能&#xff0c;这是比特币网络为了提高区块链…...

基于python+mysql的宠物领养网站系统

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括&#xff1a;首页、宠物详情页、用户中心模块。后台功能包括&#xff1a;总览、领养管理、宠物管理、分类…...

机器学习入门--门控循环单元(GRU)原理与实践

GRU模型 随着深度学习领域的快速发展&#xff0c;循环神经网络&#xff08;RNN&#xff09;已成为自然语言处理&#xff08;NLP&#xff09;等领域中常用的模型之一。但是&#xff0c;在RNN中&#xff0c;如果时间步数较大&#xff0c;会导致梯度消失或爆炸的问题&#xff0c;…...

GitHub Actions

GitHub Actions GitHub Actions 是 GitHub 提供的一种持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;解决方案。它可以让你在 GitHub 仓库中直接自动化、定制化和执行软件开发工作流程。 比如&#xff0c;当有新的推送到仓库或者新的 Pull Request…...