详解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下载文件。…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

【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…...