详解Rust多线程编程
文章目录
- 多线程模型
- 创建和管理线程
- 自定义线程行为
- 线程传递数据
- 线程间通信
- 线程池
- 错误处理与线程
- Condvar(条件变量)
- 无锁并发
- 高性能并发库
Rust的多线程编程提供了一种安全、高效的方式来进行并发操作。Rust的并发性设计原则之一是确保线程安全,同时避免运行时的开销,这使得它在多线程编程上有显著的优势。Rust通过其所有权系统、类型系统和并发原语,能够在编译时避免数据竞争和内存错误,从而使并发编程变得更安全。
多线程模型
Rust多线程模型通过所有权系统和借用检查系统确保在多线程环境中,数据不会被多个线程同时修改,数据可以通过所有权转移或引用传递给不同的线程,从而避免数据竞争。
创建和管理线程
Rust中的线程通过标准库中的std::thread模块来创建和管理。
//使用thread::spawn方法 创建线程
use std::thread;fn main() {// 创建一个线程let handle = thread::spawn(|| {println!("Hello from the thread!");});// handle.join()会阻塞主线程,直到子线程完成执行handle.join().unwrap();println!("Main thread finished.");
}
自定义线程行为
1.自定义线程名称
use std::thread;fn main() {let handle = thread::Builder::new().name("自定义线程".to_string()).spawn(|| {println!("当前线程: {:?}", thread::current().name());}).unwrap();handle.join().unwrap();
}
2.自定义线程栈大小
use std::thread;fn main() {let handle = thread::Builder::new().stack_size(4 * 1024 * 1024) // 4 MB 栈大小.spawn(|| {println!("线程运行中...");}).unwrap();handle.join().unwrap();
}
线程传递数据
在Rust中如果要从主线程将数据传递到子线程,通常通过所有权转移的方式。可以将数据通过move关键字传递给线程,或者使用Arc和Mutex来在多个线程之间共享数据。
use std::thread;fn main() {let data = String::from("Hello from main thread");//使用 move 关键字将数据的所有权传递给线程//move会捕获并转移闭包中所有的值,而不是借用它们 let handle = thread::spawn(move || {//现在 data 的所有权在子线程中println!("{}", data); });handle.join().unwrap(); // 等待线程结束
}
线程间通信
在Rust中线程间的通信通常通过以下两种方式进行:
1.Channels(通道): Rust提供了一个强类型的、基于消息传递的通道,用于线程间传递数据。Rust标准库提供了std::sync::mpsc(多生产者单消费者)模块来实现这种机制。
2.共享内存: 通过Arc(原子引用计数)和Mutex(互斥锁)组合,可以在多个线程间共享数据并保证线程安全。
使用通道进行线程间通信的调用实例如下所示:
单向通信,单个发送者和单个接收者
use std::thread;
use std::sync::mpsc;fn main() {//创建一个通道let (tx, rx) = mpsc::channel();//启动一个线程,发送数据到主线程thread::spawn(move || {let message = String::from("Hello from the thread!");tx.send(message).unwrap(); // 发送数据});//主线程接收数据let received = rx.recv().unwrap(); // 阻塞直到收到消息println!("Received: {}", received);
}
多个发送者进行通信
use std::sync::mpsc;
use std::thread;fn main() {let (tx, rx) = mpsc::channel();let tx1 = tx.clone();thread::spawn(move || {tx.send("线程1的消息").unwrap();});thread::spawn(move || {tx1.send("线程2的消息").unwrap();});for received in rx.iter().take(2) {println!("主线程接收到: {}", received);}
}
如果多个线程需要共享同一份数据,可以使用Arc和Mutex。
Arc是一个线程安全的引用计数类型,允许多个线程共享同一个数据。
Mutex是一个互斥锁,用于保护共享数据,确保只有一个线程能够同时访问数据。
use std::sync::{Arc, Mutex};
use std::thread;fn main() {//创建一个 Mutex 包装的数据//Mutex确保每次只有一个线程能修改计数器的值,Arc允许线程安全地共享该Mutex let counter = Arc::new(Mutex::new(0)); let mut handles = vec![];for _ in 0..10 {//克隆Arc获取新的引用let counter = Arc::clone(&counter); let handle = thread::spawn(move || {//如果线程未释放锁,其他线程会阻塞等待let mut num = counter.lock().unwrap(); // 获取锁*num += 1;});handles.push(handle);}for handle in handles {handle.join().unwrap();}// 输出最终的计数结果println!("Result: {}", *counter.lock().unwrap());
}
线程池
对于有大量短任务的应用,创建和销毁大量线程会有不小的性能开销。为了避免这种情况,可以使用线程池。Rust标准库本身不提供线程池,但可以使用rayon或tokio等第三方库。
1.Rayon专注于并行数据处理,适用于数据并行任务,如并行迭代器。
2.Tokio适用于异步编程,但也支持多线程和线程池。
在Cargo.toml文件中导入第三方库
[dependencies]
rayon = "1.5"
Rayon线程池示例
use rayon::prelude::*;
//Rayon库使得处理数据并行变得更加容易,它自动管理线程池,简化了多线程编程
fn main() {let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];let sum: i32 = numbers.par_iter() //并行迭代器.map(|&x| x * x) //每个元素平方.sum(); //求和println!("Sum of squares: {}", sum);
}
错误处理与线程
多线程编程中,错误处理是一个重要的方面。Rust提供了Result类型来处理错误。在多线程中,可以通过join方法来获取线程的结果,如果线程出现错误,join会返回一个Result,可以对错误进行处理。
use std::thread;fn main() {let handle = thread::spawn(|| {//模拟错误panic!("Something went wrong!");});let result = handle.join();match result {Ok(_) => println!("Thread finished successfully"),Err(e) => println!("Thread failed: {:?}", e),}
}
Condvar(条件变量)
条件变量(Condvar)是Rust中一个重要的同步原语,用于实现线程间的协调和等待机制。它允许一个线程等待某个条件变为真,而另一个线程通知条件的改变。主要用于线程间的复杂同步,结合Mutex用来等待和唤醒线程。
use std::sync::{Arc, Condvar, Mutex};
use std::thread;fn main() {let pair = Arc::new((Mutex::new(false), Condvar::new()));let pair_clone = Arc::clone(&pair);thread::spawn(move || {let (lock, cvar) = &*pair_clone;let mut started = lock.lock().unwrap();*started = true;cvar.notify_one();});let (lock, cvar) = &*pair;let mut started = lock.lock().unwrap();while !*started {started = cvar.wait(started).unwrap();}println!("线程已启动");
}
无锁并发
Rust支持无锁并发,通过原子操作或无锁数据结构避免锁的开销。使用std::sync::atomic提供的原子类型如AtomicUsize实现无锁计数
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;fn main() {let counter = AtomicUsize::new(0);let handles: Vec<_> = (0..10).map(|_| {thread::spawn(|| {for _ in 0..1000 {counter.fetch_add(1, Ordering::Relaxed);}})}).collect();for handle in handles {handle.join().unwrap();}println!("计数结果: {}", counter.load(Ordering::Relaxed));
}
高性能并发库
Rust生态中提供了许多高性能并发库,满足不同场景需求:
1.Rayon 数据并行化处理
2.Crossbeam 高性能线程间通信和无锁数据结构
3.Tokio 异步编程框架,支持高性能 I/O
4.async-std 轻量级异步运行时,类似Tokio
5.Threadpool 简单易用的线程池
相关文章:
详解Rust多线程编程
文章目录 多线程模型创建和管理线程自定义线程行为线程传递数据线程间通信线程池错误处理与线程Condvar(条件变量)无锁并发高性能并发库 Rust的多线程编程提供了一种安全、高效的方式来进行并发操作。Rust的并发性设计原则之一是确保线程安全,同时避免运行时的开销&…...
el-upload上传多个文件,一次请求,Django接收
1、:file-list"fileList" :on-change"handleChange" 将文件赋值到fileList 2、 :auto-upload"false" 手动触发上传 写个按钮点击执行这个 this.$refs.upload.submit(); 3、自己写上传,不会再触发上传成功或失败回调 4、 request.FI…...
Python实现网站资源批量下载【可转成exe程序运行】
Python实现网站资源批量下载【可转成exe程序运行】 背景介绍解决方案转为exe可执行程序简单点说详细了解下 声明 背景介绍 发现 宣讲家网 的PPT很好,作为学习资料使用很有价值,所以想下载网站的PPT课件到本地,但是由于网站限制,一…...
《JavaScript高级程序设计》读书笔记 20
感谢点赞、关注和收藏! 原始值包装类型 为了方便操作原始值,ECMAScript 提供了 3 种特殊的引用类型:Boolean、Number 和 String。每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露…...
ASP.NET Core项目中使用SqlSugar连接多个数据库的方式
之前学习ASP.NETCore及SqlSugar时都是只连接单个数据库处理数据,仅需在Program文件中添加ISqlSugarClient的单例即可(如下代码所示)。 builder.Services.AddSingleton<ISqlSugarClient>(s > {SqlSugarScope sqlSugar new SqlSugar…...
Java面试八股文(精选、纯手打)
全国内大厂Java面试高频题库 本小册内容涵盖:Java基础,JVM,多线程,数据库(MySQL/Redis)SSM,Dubbo,网络,MQ,Zookeeper,Netty,微服务&a…...
工程设计行业内外网文件交换解决方案:FileLink助力高效、安全的跨网协作
在工程设计行业,文件交换和信息共享是项目顺利推进的关键。尤其是涉及复杂的设计图纸、技术方案、合同文件等重要资料时,如何确保文件在内外网之间的高效、安全传输,直接影响到项目的进度和质量。FileLink正是为此而生,它为工程设…...
Qt 2D绘图之三:绘制文字、路径、图像、复合模式
参考文章链接: Qt 2D绘图之三:绘制文字、路径、图像、复合模式 绘制文字 除了绘制图形以外,还可以使用QPainter::darwText()函数来绘制文字,也可以使用QPainter::setFont()设置文字所使用的字体,使用QPainter::fontInfo()函数可以获取字体的信息,它返回QFontInfo类对象…...
配置宝塔php curl 支持http/2 发送苹果apns消息推送
由于宝塔面板默认的php编译的curl未加入http2的支持,如果服务需要使用apns推送等需要http2.0的访问就会失败,所以重新编译php让其支持http2.0 编译方法: 一、安装nghttp2 git clone https://github.com/tatsuhiro-t/nghttp2.git cd nghttp…...
Redis服务配置文件 redis.conf 更新修改配置参数说明
场景: 在安装redis服务中,默认的配置项通常不能实际使用,需要修改一些配置参数 修改配置参数 1、拿到 redis.cnf 文件,此文件通常在 redis 项目源码的第一级目录下 2、修改配置内容,主要修改项如下 protect…...
Android 俩个主题的不同之处 “Theme.AppCompat vs android:Theme.Material.Light.NoActionBar”
这两种主题继承关系有明显的不同,具体区别如下: Theme.AppCompat vs android:Theme.Material.Light.NoActionBar Theme.AppCompat 是 AppCompat 支持库提供的主题,目的是提供向后兼容的支持,适用于较早版本的 Android(…...
Redis+Caffeine 多级缓存数据一致性解决方案
RedisCaffeine 多级缓存数据一致性解决方案 背景 之前写过一篇文章RedisCaffeine 实现两级缓存实战,文章提到了两级缓存RedisCaffeine可以解决缓存雪等问题也可以提高接口的性能,但是可能会出现缓存一致性问题。如果数据频繁的变更,可能会导…...
vscode ctrl+/注释不了css
方式一.全部禁用插件排查问题. 方式二.打开首选项的json文件,注释掉setting.json,排查是哪一行配置有问题. 我的最终问题:需要将 "*.vue": "vue",改成"*.vue": "html", "files.associations": { // "*.vue": &qu…...
《山海经》:北山
《山海经》:北山 北山一经单狐山求如山(水马:形状与马相似,滑鱼:背部红色)带山(䑏疏:似马,一只角,鵸鵌:状乌鸦五彩斑斓,儵鱼ÿ…...
oracle中删除指定前缀的表
近期接手做的项目,发觉数据库中有许多多余的表。究其原因,应该是同事贪图方便,将过去做过的项目复制粘贴,然后修修改改。包括数据库也是克隆过来的,然后又没有删除本项目多余的表,结果经过几个轮回…...
解决 Flutter Dio合并请求多个接口,如果一个接口500,那么导致其他请求不在执行
Flutter Dio如何自定义拦截异常 应用场景 我们一般会统一拦截DioExceptionType 如400,403,500 等错误 但有时候,有个地方合并请求多个接口,如果一个接口500,那么导致其他请求不在执行,因为统一拦截了500&…...
The selected directory is not a valid home for Go SDK
在idea里配置go语言的环境时,选择go语言的安装目录,一直提示这个 The selected directory is not a valid home for Go SDK后来查了一下,发现原来idea识别不出来 需要改一下配置文件,找到go环境的安装目录,我是默认安…...
基于云模型的车辆行驶速度估计算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于云模型的车辆行驶速度估计算法matlab仿真。在智能交通系统中,车辆行驶速度的准确估计对于交通流量监测、安全预警、自动驾驶辅助等方面具有极为重…...
MySQL有哪些日志?
MySQL主要有三种日志:undo log、redo log、binlog。前两种是InnoDB特有的,binlog是MySQL的Server层中的。 Buffer Pool buffer pool是MySQL的缓冲池,里面存储了数据页、索引页、undo页等(与数据库不一致的即为脏页)。…...
Axios:现代JavaScript HTTP客户端
在当今的Web开发中,与后端服务进行数据交换是必不可少的。Axios是一个基于Promise的HTTP客户端,用于浏览器和node.js,它提供了一个简单的API来执行HTTP请求。本文将介绍Axios的基本概念、优势、安装方法、基本用法以及如何使用Axios下载文件。…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
