rust学习-线程
Rust 标准库只提供了 1:1 线程模型
Rust 是较为底层的语言,如果愿意牺牲性能来换取抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,使用实现了 M:N 线程模型的 crate
示例
use std::thread;
use std::time::Duration;fn main() {// 调用 thread::spawn 函数并传递一个闭包,来创建线程let handle = 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));}// 等待结束// // `unwrap` 在接收到 `None` 时将返回 `panic`handle.join().unwrap();
}
在一个线程中使用另一个线程的数据
在参数列表前使用 move 关键字强制闭包获取其使用的环境值的所有权。
这个技巧在创建新线程将值的所有权从一个线程移动到另一个线程时最为实用
use std::thread;fn main() {let v = vec![1, 2, 3]let handle = thread::spawn(|| {// `v` is borrowed here// Rust 不知道这个新建线程会执行多久,所以无法知晓 v 的引用是否一直有效println!("Here's a vector: {:?}", v);});// 一个具有闭包的线程,尝试使用一个在主线程中被回收的引用 v// drop(v);handle.join().unwrap();
}
force the closure to take ownership of v (and any other referenced variables), use the move keyword
use std::thread;fn main() {let v = vec![1, 2, 3];// 增加 move 关键字,强制闭包获取其使用的值的所有权let handle = thread::spawn(move || {println!("Here's a vector: {:?}", v);});handle.join().unwrap();// value borrowed here after moveprintln!("Here's a vector: {:?}", v); // 这里编译报错
}
use std::thread;fn main() {let v = vec![1, 2, 3]let handle = thread::spawn(move || {// `v` is borrowed hereprintln!("Here's a vector: {:?}", v);});handle.join().unwrap();
}
channel
mpsc:multiple producer, single consumer
基本用法
use std::thread;
// 多发单收模型
// multiple producer, single consumer
use std::sync::mpsc;fn main() {// tx 是发送端,rx是接收端let (tx, rx) = mpsc::channel();thread::spawn(move || {let val = String::from("hi");tx.send(val).unwrap();});// 主线程阻塞let received = rx.recv().unwrap();println!("Got: {}", received);
}
try_recv 不会阻塞,相反它立刻返回一个 Result<T, E>:Ok 值包含可用的信息,而 Err 值代表此时没有任何消息
示例
use std::thread;
use std::sync::mpsc;
use std::time::Duration;fn main() {let (tx, rx) = mpsc::channel();thread::spawn(move || {let vals = vec![String::from("hi"),String::from("from"),String::from("the"),String::from("thread"),];for val in vals {tx.send(val).unwrap();thread::sleep(Duration::from_secs(1));}});// 打印出四个消息后退出for received in rx {println!("Got: {}", received);}
}
所有权被转移
use std::thread;
use std::sync::mpsc;fn main() {let (tx, rx) = mpsc::channel();thread::spawn(move || {let val = String::from("hi");tx.send(val).unwrap();// 编译失败// value borrowed here after moveprintln!("val is {}", val);});let received = rx.recv().unwrap();println!("Got: {}", received);
}
多生产者
通过克隆通道的发送端来做到
类似于Go中的:“不要通过共享内存来通讯;而是通过通讯来共享内存
(“Do not communicate by sharing memory; instead, share memory by communicating.”)
use std::thread;
use std::sync::mpsc;
use std::time::Duration;fn main() {let (tx, rx) = mpsc::channel();let tx1 = tx.clone();thread::spawn(move || {let vals = vec![String::from("hi"),String::from("from"),String::from("the"),String::from("thread"),];for val in vals {tx1.send(val).unwrap();thread::sleep(Duration::from_secs(1));}});thread::spawn(move || {let vals = vec![String::from("more"),String::from("messages"),String::from("for"),String::from("you"),];for val in vals {tx.send(val).unwrap();thread::sleep(Duration::from_secs(1));}});for received in rx {println!("Got: {}", received);}println!("finish")
}
Mutex 互斥器
使用方式
cat main.rs
use std::sync::Mutex;fn main() {let m = Mutex::new(5);{// lock 调用 返回 一个叫做 MutexGuard 的智能指针// 这个智能指针实现了 Deref 来指向其内部数据// 也提供了一个 Drop 实现当 MutexGuard 离开作用域时自动释放锁let mut num = m.lock().unwrap();*num = 6;}// 打印内容如下 Mutex { data: 6, poisoned: false, .. }println!("m = {:?}", m);
}
糟糕例子
use std::sync::Mutex;
use std::thread;fn main() {let counter = Mutex::new(0);let mut handles = vec![];for _ in 0..10 {// value moved into closure here, in previous iteration of loop// 编译失败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());
}
解决方式-Arc
Arc 和 Rc 有着相同的 API
Arc 一个类似 Rc 并可以安全的用于并发环境的类型
字母 “a” 代表 原子性(atomic),所以这是一个原子引用计数
为什么不是所有标准库中的类型都默认使用 Arc 实现?
线程安全带有性能惩罚,只在必要时才为此买单
use std::sync::{Mutex, Arc};
use std::thread;fn main() {// counter 是不可变的,因为前面没有mut// 意味着Mutex<T> 提供了内部可变性// 就像使用 RefCell<T> 可以改变 Rc<T> 中的内容那样// 使用 Mutex<T> 来改变 Arc<T> 中的内容 let counter = Arc::new(Mutex::new(0));let mut handles = vec![];for _ in 0..10 {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();}// 打印结果为10println!("Result: {}", *counter.lock().unwrap());
}
Mutex 也有造成 死锁(deadlock) 的风险
使用 Sync 和 Send trait 的可扩展并发
语言本身对并发知之甚少。上述讨论的几乎所有内容都属于标准库,而不是语言本身的内容,可以编写自己的或使用别人编写的并发功能。
但有两个并发概念是内嵌于语言中的:std::marker 中的 Sync 和 Send trait
通过 Send 允许在线程间转移所有权
【Send 标记 trait】
表明类型的所有权可以在线程间传递
几乎所有的 Rust 类型都是Send 的,但是也有例外,比如Rc
如果克隆了 Rc 的值并尝试将克隆的所有权转移到另一个线程,这两个线程都可能同时更新引用计数,Rc 被实现为用于单线程场景,所以不用担心多线程下的引用计数问题。
糟糕例子
the trait Send is not implemented for Rc<Mutex<i32>>
use std::rc::Rc;
use std::sync::Mutex;
use std::thread;fn main() {let counter = Rc::new(Mutex::new(0));let mut handles = vec![];// Rc<Mutex<i32>>` cannot be sent between threads safelyfor _ in 0..10 {let counter = Rc::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());
}
解决办法
使用原子引用计数 Arc
Sync 允许多线程访问
【Sync 标记 trait】
一个实现了 Sync 的类型可以安全的在多个线程中拥有其值的引用
对于任意类型 T,如果 &T(T 的引用)是 Send 的话 T 就是 Sync 的
其引用就可以安全的发送到另一个线程
Rc 不是 Sync的
RefCell 和 Cell 系列类型不是 Sync 的
RefCell 在运行时所进行的借用检查也不是线程安全的
Mutex 是 Sync 的,可以被用来在多线程中共享访问
相关文章:
rust学习-线程
Rust 标准库只提供了 1:1 线程模型 Rust 是较为底层的语言,如果愿意牺牲性能来换取抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,使用实现了 M:N 线程模型的 crate 示例 use std::thread; use std::time::Duration;fn main() …...
题目:2180.统计各位数字之和为偶数的整数个数
题目来源: leetcode题目,网址:2180. 统计各位数字之和为偶数的整数个数 - 力扣(LeetCode) 解题思路: 暴力遍历即可。 解题代码: class Solution {public int countEven(int num) {int re…...
3dsmax制作一个机器人
文章目录 建模身子:眼睛:头饰:肩膀手臂腿调整细节 渲染导出objMarmoset Toolbag 3.08渲染给眼睛添加材质,设置为自发光添加背景灯光 建模 身子: 眼睛: 头饰: 肩膀 手臂 腿 调整细节 渲染 导出…...
C++的类型转换运算符:reinterpret_cast
C的类型转换运算符:reinterpret_cast reinterpret_cast 是 C 中与 C 风格类型转换最接近的类型转换运算符。它让程序员能够将一种对象类型转换为另一种,不管它们是否相关;也就是说,它使用如下所示的语法强制重新解释类型…...
flask中的cookies介绍
flask中的cookies介绍 “Cookie” 在 web 开发中是一种非常重要的技术,用于在客户端(即用户的浏览器)存储信息,以便在多个页面和多个访问会话之间保持状态。Cookies 通常用于记住用户的登录信息,跟踪用户在站点上的浏…...
adnroid 11. 0 Activity启动流程图解
从Launcher到ActivityTaskManager 从ActivityTaskManagerService 到 ApplicationThread 从ApplicationThread到onCreate...
了解Unity编辑器之组件篇Physics(四)
Physics:用于处理物理仿真和碰撞检测。它提供了一组功能强大的工具和算法,用于模拟真实世界中的物理行为,使游戏或应用程序更加真实和可信。 主要用途包括: 碰撞检测:Unity Physics 提供了高效的碰撞检测算法&#x…...
“数字中华 点亮未来”中华线上客户节 盛大开幕
2023年是中华保险数字化转型落地之年,峥嵘37载,中华保险在数字化转型上已经涌现了一批彰显辨识度、具有影响力的应用成果。7月15日,中华保险围绕数字化转型之路开展以“数字中华 点亮未来”为主题的37周年线上客户节活动,倾力打造…...
中文分词入门:使用IK分词器进行文本分词(附Java代码示例)
1. 介绍 中文分词是将连续的中文文本切分成一个个独立的词语的过程,是中文文本处理的基础。IK分词器是一个高效准确的中文分词工具,采用了"正向最大匹配"算法,并提供了丰富的功能和可定制选项。 2. IK分词器的特点 细粒度和颗粒…...
CTFSHOW web 信息收集
web入门的刷题 web1 教我们多看看源代码 web2 抓包 web3 抓包 web4 robots.txt robots.txt web5 phps源代码泄露 phps 就是php的源代码 用户无法访问 php 只可以通过phps来访问 web6 源代码备份 web7 git web8 svn web9 swp /index.php.swp web10 cookie web11 查域名…...
速锐得开发社区-新一代汽车网络通信技术CAN FD的特点归纳
随着汽车工业的快速发展,汽车逐渐走向智能化,功能也越来越丰富,例如特斯拉、比亚迪、理想汽车为代表,在车载导航、驻车雷达、胎压监测、倒车影像、无钥匙启动、定速巡航、自动泊车、高级辅助驾驶系统、自动驾驶、域控制器、智能网…...
Android adb shell 查看App内存(java堆内存/vss虚拟内存/详细的内存状况/内存快照hprof)和系统可用内存
1.adb shell 获取app 进程的pid adb shell "ps|grep com.xxx包名"根据某个渠道包,去查询对应的pid,如下所示: 2.通过adb shell 查看设备的java dalvik 堆内存的最大值 执行命令行: adb shell getprop dalvik.vm.h…...
java篇 类的进阶0x0A:万类之祖:Object 类
文章目录 万类之祖:Object 类hashCode() 与 equals()hashCode() 方法equals() 方法 vs. equals()String 的 equals() 为什么要重写 hashCode 和 equals 方法重写(覆盖)前 hashCode() 和 equals() 的作用什么情况下需要重写(覆盖&a…...
AVFoundation - 音频录制
文章目录 需要调用到麦克风方法,别忘记添加 Privacy - Microphone Usage Description @interface AudioRecorder ()<AVAudioRecorderDelegate>@property (strong, nonatomic) AVAudioRecorder *recorder;@end@implementation AudioRecorder- (void...
Jmeter+MySQL链接+JDBC Connection配置元件+使用
参考大大的博客学习:怎么用JMeter操作MySQL数据库?看完秒懂!_jmeter mysql_程序员馨馨的博客-CSDN博客 注:里面所有没打码的都是假数据,麻烦大家自行修改正确的信息。 一、背景 需要取数据库中的值,作为…...
统一观测丨使用 Prometheus 监控 Cassandra 数据库最佳实践
作者:元格 本篇内容主要包括四部分:Cassandra 概览介绍、常见关键指标解读、常见告警规则解读、如何通过 Prometheus 建立相应监控体系。 Cassandra 简介 Cassandra 是什么? Apache Cassandra 是一个开源、分布式、去中心化、弹性可伸缩、…...
Hive视图
hive的视图 简介 hive的视图简单理解为逻辑上的表hive只支持逻辑视图,不支持物化视图视图存在的意义 对数据进行局部暴露(涉及隐私的数据不暴露)简化复杂查询 创建视图: create view if not exists v_1 as select uid,movie f…...
node中使用jsonwebtoken实现身份认证
在现代web应用中,用户身份认证是非常重要且必不可少的一环。而使用Node.js和Express框架,可以方便地实现用户身份认证。而在这个过程中,jsonwebtoken这个基于JWT协议的模块可以帮助我们实现安全且可靠的身份认证机制,可以让我们轻…...
pyspark笔记:读取 处理csv文件
pyspark cmd上的命令 1 读取文件 1.1 基本读取方式 注意读取出来的格式是Pyspark DataFrame,不是DataFrame,所以一些操作上是有区别的 1.1.1 format DataFrame spark.read.format("csv").option(name,value).load(path) format表示读取…...
多租户分缓存处理
多租户redis缓存分租户处理 那么数据库方面已经做到了拦截,但是缓存还是没有分租户,还是通通一个文件夹里, 想实现上图效果,global文件夹里存的是公共缓存。 首先,那么就要规定一个俗称,缓存名字带有globa…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
el-amap-bezier-curve运用及线弧度设置
文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...
