当前位置: 首页 > 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…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...