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

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 是较为底层的语言&#xff0c;如果愿意牺牲性能来换取抽象&#xff0c;以获得对线程运行更精细的控制及更低的上下文切换成本&#xff0c;使用实现了 M:N 线程模型的 crate 示例 use std::thread; use std::time::Duration;fn main() …...

题目:2180.统计各位数字之和为偶数的整数个数

​​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;2180. 统计各位数字之和为偶数的整数个数 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 暴力遍历即可。 解题代码&#xff1a; class Solution {public int countEven(int num) {int re…...

3dsmax制作一个机器人

文章目录 建模身子&#xff1a;眼睛&#xff1a;头饰&#xff1a;肩膀手臂腿调整细节 渲染导出objMarmoset Toolbag 3.08渲染给眼睛添加材质&#xff0c;设置为自发光添加背景灯光 建模 身子&#xff1a; 眼睛&#xff1a; 头饰&#xff1a; 肩膀 手臂 腿 调整细节 渲染 导出…...

C++的类型转换运算符:reinterpret_cast

C的类型转换运算符&#xff1a;reinterpret_cast reinterpret_cast 是 C 中与 C 风格类型转换最接近的类型转换运算符。它让程序员能够将一种对象类型转换为另一种&#xff0c;不管它们是否相关&#xff1b;也就是说&#xff0c;它使用如下所示的语法强制重新解释类型&#xf…...

flask中的cookies介绍

flask中的cookies介绍 “Cookie” 在 web 开发中是一种非常重要的技术&#xff0c;用于在客户端&#xff08;即用户的浏览器&#xff09;存储信息&#xff0c;以便在多个页面和多个访问会话之间保持状态。Cookies 通常用于记住用户的登录信息&#xff0c;跟踪用户在站点上的浏…...

adnroid 11. 0 Activity启动流程图解

从Launcher到ActivityTaskManager 从ActivityTaskManagerService 到 ApplicationThread 从ApplicationThread到onCreate...

了解Unity编辑器之组件篇Physics(四)

Physics&#xff1a;用于处理物理仿真和碰撞检测。它提供了一组功能强大的工具和算法&#xff0c;用于模拟真实世界中的物理行为&#xff0c;使游戏或应用程序更加真实和可信。 主要用途包括&#xff1a; 碰撞检测&#xff1a;Unity Physics 提供了高效的碰撞检测算法&#x…...

“数字中华 点亮未来”中华线上客户节 盛大开幕

2023年是中华保险数字化转型落地之年&#xff0c;峥嵘37载&#xff0c;中华保险在数字化转型上已经涌现了一批彰显辨识度、具有影响力的应用成果。7月15日&#xff0c;中华保险围绕数字化转型之路开展以“数字中华 点亮未来”为主题的37周年线上客户节活动&#xff0c;倾力打造…...

中文分词入门:使用IK分词器进行文本分词(附Java代码示例)

1. 介绍 中文分词是将连续的中文文本切分成一个个独立的词语的过程&#xff0c;是中文文本处理的基础。IK分词器是一个高效准确的中文分词工具&#xff0c;采用了"正向最大匹配"算法&#xff0c;并提供了丰富的功能和可定制选项。 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的特点归纳

随着汽车工业的快速发展&#xff0c;汽车逐渐走向智能化&#xff0c;功能也越来越丰富&#xff0c;例如特斯拉、比亚迪、理想汽车为代表&#xff0c;在车载导航、驻车雷达、胎压监测、倒车影像、无钥匙启动、定速巡航、自动泊车、高级辅助驾驶系统、自动驾驶、域控制器、智能网…...

Android adb shell 查看App内存(java堆内存/vss虚拟内存/详细的内存状况/内存快照hprof)和系统可用内存

1.adb shell 获取app 进程的pid adb shell "ps|grep com.xxx包名"根据某个渠道包&#xff0c;去查询对应的pid&#xff0c;如下所示&#xff1a; 2.通过adb shell 查看设备的java dalvik 堆内存的最大值 执行命令行&#xff1a; adb shell getprop dalvik.vm.h…...

java篇 类的进阶0x0A:万类之祖:Object 类

文章目录 万类之祖&#xff1a;Object 类hashCode() 与 equals()hashCode() 方法equals() 方法 vs. equals()String 的 equals() 为什么要重写 hashCode 和 equals 方法重写&#xff08;覆盖&#xff09;前 hashCode() 和 equals() 的作用什么情况下需要重写&#xff08;覆盖&a…...

AVFoundation - 音频录制

文章目录 需要调用到麦克风方法,别忘记添加 Privacy - Microphone Usage Description @interface AudioRecorder ()<AVAudioRecorderDelegate>@property (strong, nonatomic) AVAudioRecorder *recorder;@end@implementation AudioRecorder- (void...

Jmeter+MySQL链接+JDBC Connection配置元件+使用

参考大大的博客学习&#xff1a;怎么用JMeter操作MySQL数据库&#xff1f;看完秒懂&#xff01;_jmeter mysql_程序员馨馨的博客-CSDN博客 注&#xff1a;里面所有没打码的都是假数据&#xff0c;麻烦大家自行修改正确的信息。 一、背景 需要取数据库中的值&#xff0c;作为…...

统一观测丨使用 Prometheus 监控 Cassandra 数据库最佳实践

作者&#xff1a;元格 本篇内容主要包括四部分&#xff1a;Cassandra 概览介绍、常见关键指标解读、常见告警规则解读、如何通过 Prometheus 建立相应监控体系。 Cassandra 简介 Cassandra 是什么&#xff1f; Apache Cassandra 是一个开源、分布式、去中心化、弹性可伸缩、…...

Hive视图

hive的视图 简介 hive的视图简单理解为逻辑上的表hive只支持逻辑视图&#xff0c;不支持物化视图视图存在的意义 对数据进行局部暴露&#xff08;涉及隐私的数据不暴露&#xff09;简化复杂查询 创建视图&#xff1a; create view if not exists v_1 as select uid,movie f…...

node中使用jsonwebtoken实现身份认证

在现代web应用中&#xff0c;用户身份认证是非常重要且必不可少的一环。而使用Node.js和Express框架&#xff0c;可以方便地实现用户身份认证。而在这个过程中&#xff0c;jsonwebtoken这个基于JWT协议的模块可以帮助我们实现安全且可靠的身份认证机制&#xff0c;可以让我们轻…...

pyspark笔记:读取 处理csv文件

pyspark cmd上的命令 1 读取文件 1.1 基本读取方式 注意读取出来的格式是Pyspark DataFrame&#xff0c;不是DataFrame&#xff0c;所以一些操作上是有区别的 1.1.1 format DataFrame spark.read.format("csv").option(name,value).load(path) format表示读取…...

多租户分缓存处理

多租户redis缓存分租户处理 那么数据库方面已经做到了拦截&#xff0c;但是缓存还是没有分租户&#xff0c;还是通通一个文件夹里&#xff0c; 想实现上图效果&#xff0c;global文件夹里存的是公共缓存。 首先&#xff0c;那么就要规定一个俗称&#xff0c;缓存名字带有globa…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...