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

利用Nanbeige 4.1-3B构建智能数据库查询优化器原型

利用Nanbeige 4.1-3B构建智能数据库查询优化器原型 最近在捣鼓一个挺有意思的项目&#xff0c;想看看大模型能不能帮我们解决一个老难题&#xff1a;数据库查询优化。这事儿听起来有点硬核&#xff0c;但说白了&#xff0c;就是让AI来当数据库的“老中医”&#xff0c;给它看看…...

HY-MT1.5-1.8B提效实战:批量SRT翻译系统部署步骤

HY-MT1.5-1.8B提效实战&#xff1a;批量SRT翻译系统部署步骤 本文介绍如何快速部署HY-MT1.5-1.8B翻译模型&#xff0c;实现SRT字幕文件的批量翻译处理&#xff0c;大幅提升多语言字幕制作效率。 1. 环境准备与模型下载 在开始部署前&#xff0c;我们先简单了解下HY-MT1.5-1.8B…...

I.MX6ULL GPIO配置避坑指南:HYS、PUS、DSE这些寄存器位到底怎么设?

I.MX6ULL GPIO配置实战手册&#xff1a;寄存器位场景化解析与避坑策略 如果你正在使用I.MX6ULL开发嵌入式系统&#xff0c;GPIO配置可能是你遇到的第一个"拦路虎"。与常见的STM32不同&#xff0c;I.MX6ULL的GPIO配置寄存器充满了各种缩写——HYS、PUS、PUE、PKE、ODE…...

OpenClaw+百川2-13B-4bits量化模型:个人知识管理自动化方案

OpenClaw百川2-13B-4bits量化模型&#xff1a;个人知识管理自动化方案 1. 为什么需要自动化知识管理 作为一个长期与技术文档打交道的开发者&#xff0c;我的知识库在过去三年膨胀到了2000篇杂乱无章的Markdown文件。每次查找资料时&#xff0c;要么记不清文件名&#xff0c;…...

OpenClaw低配优化:千问3.5-9B在4GB内存设备运行

OpenClaw低配优化&#xff1a;千问3.5-9B在4GB内存设备运行 1. 为什么需要低配优化&#xff1f; 去年冬天&#xff0c;我在一台老旧的MacBook Air上第一次尝试部署OpenClaw时&#xff0c;遇到了内存不足的报错。这台2017年的设备只有4GB内存&#xff0c;而默认配置的千问3.5-…...

OpenClaw 接入微信,QQ,飞书的正确方法-周红伟

OpenClaw 接入微信 OpenClaw&#xff08;原 Clawdbot&#xff09;是一个开源、本地优先的 AI 代理网关&#xff0c;能让大模型在你的电脑/服务器上 7X24 小时运行&#xff0c;支持直接操作电脑、浏览网页、执行命令&#xff0c;还能无缝接入飞书、Telegram、Discord 等聊天平台…...

UI 动效设计原则:让界面呼吸起来

UI 动效设计原则&#xff1a;让界面呼吸起来 动效不是装饰&#xff0c;而是交互的语言。掌握这些原则&#xff0c;让你的设计会"说话"。 一、动效的本质 作为一名把代码当散文写的 UI 匠人&#xff0c;我始终认为动效是界面的灵魂。一个好的动效应该像呼吸一样自然—…...

手把手教你写一个TextMeshProUGUI尺寸控制器:搞定聊天框、公告板等动态文本布局

手把手构建TextMeshProUGUI智能尺寸控制器&#xff1a;从原理到实战优化 在Unity的UI开发中&#xff0c;文本组件的动态布局一直是让开发者头疼的问题。想象这样一个场景&#xff1a;你的游戏聊天系统需要根据消息长度自动调整气泡大小&#xff0c;公告板要限制文本显示区域避免…...

Go语言开发的my2sql vs Python版binlog2sql:实测百GB级binlog解析性能对比

Go语言my2sql与Python版binlog2sql百GB级性能对决&#xff1a;技术选型终极指南 当数据库表里的数据被误删时&#xff0c;你的第一反应是什么&#xff1f;是立即联系备份恢复&#xff0c;还是尝试从binlog中找回丢失的记录&#xff1f;对于处理过生产环境数据事故的DBA来说&am…...

好写作AI“学术清道夫”:论文查重,为学术诚信保驾护航

在学术的浩瀚星空中&#xff0c;论文是学子们展示智慧与研究成果的璀璨星辰。然而&#xff0c;随着学术交流的日益频繁&#xff0c;论文抄袭、剽窃等不端行为也时有发生&#xff0c;这不仅损害了学术的公正性和严肃性&#xff0c;也阻碍了学术的健康发展。在这样的背景下&#…...