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

Rust多线程编程

Rust多线程编程

文章目录

  • Rust多线程编程
    • 使用线程模块
    • 创建线程
    • 线程传参
      • 闭包(匿名函数)
        • 值捕获
        • 不可变引用捕获
        • 可变引用捕获
      • 线程闭包传参
      • 更优雅地传参
    • 回收线程
    • 线程同步和通信
      • channel 通道
      • mutex 互斥锁
      • Barrier 栅栏
      • Atomic Types 原子类型

使用线程模块

rust标准库中提供了线程相关支持,直接引用即可使用:

use std::thread;

创建线程

使用spawn方法创建一个线程。如:

use std::thread;/* 引用线程模块 */
use std::time::Duration;
fn main() {std::thread::spawn(thread_function);loop {thread::sleep(Duration::from_secs(1));/* sleep 1s */println!("main thread running..");}
}fn thread_function(){loop {thread::sleep(Duration::from_secs(1));/* sleep 1s */println!("demo thread running..");}
}

创建后线程自动运行:

boys@server:~/rust_study/demo$ cargo runCompiling demo v0.1.0 (/home/boys/rust_study/demo)Finished dev [unoptimized + debuginfo] target(s) in 0.39sRunning `target/debug/demo`
main thread running..
demo thread running..
main thread running..
demo thread running..
main thread running..
demo thread running..
^C
boys@server:~/rust_study/demo$ 

线程传参

默认的spawn方法传递不了参数。如果要为线程传递参数,需要使用匿名函数作为spawn的参数,匿名函数也称为闭包。

闭包(匿名函数)

闭包的基本语法:

|param1, param2, ...| {// 函数体
}

使用闭包的好处是可以捕获函数体外的变量,传递到函数体内。

按捕获变量方式可以分为:

  • 值捕获
  • 普通引用捕获
  • 可变引用捕获

值捕获

值捕获的方式会变量的所有权会转移到闭包内,外部无法再使用。如:

fn main(){let str = String::from("hello");let closure_print_string = move ||{/* move表明函数体内捕获的变量使用值捕获方式 */println!("number = {}", str);/* 使用值捕获方式捕获外部的str */};closure_print_string();println!("test for str: {}", str);/* 值捕获方式,str的所有权已经被转移到闭包内,这里无法再使用 */
}

不可变引用捕获

闭包会自动识别捕获的变量类型。根据函数体内捕获的变量的类型,确认捕获方式。

不可变变量按照不可变引用方式捕获使用,因此无法修改原变量值,只能访问变量值。如:

fn main(){let str = String::from("hello");/* str是不可变 */let closure = ||{println!("str: {}", str);// str.push_str("world");/* 不可变的变量按不可变引用方式捕获,此处修改了变量值会报错 */};closure();println!("test for str: {}", str);
}

可变引用捕获

将外部变量变为可变变量,即可在闭包函数体内修改变量的值。

fn main(){let mut str = String::from("hello");/* str是不可变 */let mut closure = ||{println!("before push, str: {}", str);str.push_str(" world");/* 不可变的变量按不可变引用方式捕获,此处修改了变量值会报错 */};closure();println!("test for str: {}", str);
}

同时闭包的类型也要定义为mut可变的。只要捕获外部的可变变量,都是定义为mut的。如:

fn main(){let num = 123;let mut str = String::from("hello");/* str是不可变 */let mut closure = ||{println!("num: {}", num);/* num不可变 */println!("before push, str: {}", str);str.push_str(" world");/* 不可变的变量按不可变引用方式捕获,此处修改了变量值会报错 */};closure();println!("test for str: {}", str);
}

线程闭包传参

现在就可以使用闭包的方法,将外部参数传递给线程了。

如:

fn main(){let word = "what are words";let thread_handle = std::thread::spawn(move ||{println!("just print 'word': {}", word);});thread_handle.join().unwrap();
}

这里必须使用值捕获方式,将字符串引用变量str的所有权转移到闭包内。因为编译时会检查闭包和当前函数的声明周期,发现闭包可能会在当前的函数结束后运行,因为闭包传递到了另一个线程。

closure may outlive the current function, but it borrows `word`, which is owned by the current function

更优雅地传参

通过闭包虽然可以传递外部参数到另一个线程,但闭包一般都用来实现比较简单的功能。对于线程来说,会有比较复杂的功能,所以更优雅的方式还是使用函数对一个线程做封装后,再闭包传递参数创建线程。如:

use std::time::Duration;
struct ThreadParam{thread_name: String
}
fn demo_thread(param: ThreadParam){for _ in 0..3{/* 使用下划线作为迭代变量,避免编译警告 */std::thread::sleep(Duration::from_secs(1));println!("thread's name: {}", param.thread_name);}
}
fn main(){let thread_arguments = ThreadParam{thread_name: String::from("demo_thread")};let thread_handle = std::thread::spawn(move ||{demo_thread(thread_arguments);});thread_handle.join().unwrap();/* 回收线程,防止主线程退出看不到线程效果 */
}

回收线程

使用join方法回收线程资源。如:

use std::thread;/* 引用线程模块 */
use std::time::Duration;
fn main() {let thread_handle = std::thread::spawn(thread_function);thread_handle.join().unwrap();println!("thread exit.");
}fn thread_function(){let mut count = 1;loop {thread::sleep(Duration::from_secs(1));/* sleep 1s */println!("demo thread run {} time..", count);count = count + 1;if count > 3 {break;}}
}
boys@server:~/rust_study/demo$ cargo runFinished dev [unoptimized + debuginfo] target(s) in 0.00sRunning `target/debug/demo`
demo thread run 1 time..
demo thread run 2 time..
demo thread run 3 time..
thread exit.

线程同步和通信

rust线程同步和通信提供了几种自带的机制:

  • channel(通道)
  • Mutex(互斥锁)
  • 原子类型(Atomic Types)
  • 条件变量(Condition Variable)
  • 原子引用计数(Arc)
  • 栅栏(Barrier)

channel 通道

通道只能使用在多生产者、单消费者的场景下,所在模块是 std::sync::mpsc,mpsc是"Multi-Producer, Single-Consumer" 的缩写,即多生产者单消费者。

use std::sync::mpsc;

使用示例:

fn main(){let (sender, receiver) = std::sync::mpsc::channel();let sender1 = sender.clone();/* 通过克隆2个sender传递到线程中 */let sender2 = sender.clone();let sender1_thread_handle = std::thread::spawn(move||{std::thread::sleep(std::time::Duration::from_secs(1));sender1.send("hello").unwrap();});let sender2_thread_handle = std::thread::spawn(move||{std::thread::sleep(std::time::Duration::from_secs(2));sender2.send("bye").unwrap();});let mut is_connected = false;loop {let recv_data = receiver.recv().unwrap();if recv_data == "hello"{println!("recv 'hello', connected");is_connected = true;}if is_connected == true{if recv_data == "bye"{println!("recv 'bye', disconnected");break;}}}sender1_thread_handle.join().unwrap();sender2_thread_handle.join().unwrap();
}

mutex 互斥锁

导入模块:

use std::sync::Mutex;

Rust 中的互斥锁(Mutex)是一个泛型类型。它被定义为 std::sync::Mutex<T>,其中的 T 是要保护的共享数据的类型。初始化时会自动判断数据类型。

下面是使用Mutex对整形变量做互斥访问的示例:

use std::sync::{Mutex, Arc};
use std::thread;fn main() {let data = Arc::new(Mutex::new(42));let thread1_data = Arc::clone(&data);let handle1 = thread::spawn(move || {// 使用 thread1_data 进行操作let mut value = thread1_data.lock().unwrap();*value += 1;println!("thread1 Value: {}", *value);});let thread2_data = Arc::clone(&data);let handle2 = thread::spawn(move || {// 使用 thread2_data 进行操作let value = thread2_data.lock().unwrap();println!("thread2 Value: {}", *value);});handle1.join().unwrap();handle2.join().unwrap();
}

这里使用了Arc原子引用计数类型,实现多个线程共享互斥锁的所有权。

Barrier 栅栏

在 Rust 中,你可以使用 Barrier 类型来实现线程栅栏。 Barrier 允许一组线程都到达一个点后再同时继续执行。

示例:

use std::sync::{Arc, Barrier};
use std::thread;fn main() {let barrier = Arc::new(Barrier::new(3)); // 创建一个包含3个参与者的栅栏for i in 0..3 {/* 创建3个线程 *//* 使用Arc原子引用计数将barrier共享给所有线程 */let barrier_clone = Arc::clone(&barrier);thread::spawn(move || {println!("Thread {} before barrier", i);barrier_clone.wait(); // 所有线程都到达此处后会同时继续执行println!("Thread {} after barrier", i);});}thread::sleep(std::time::Duration::from_secs(2)); // 等待足够的时间以确保所有线程完成println!("Main thread");
}

Atomic Types 原子类型

Rust中常用的原子类型有:

  1. AtomicBool 原子布尔类型
  2. AtomicI32 原子整数类型
  3. AtomicPtr 原子指针类型

原子类型是不可分割的类型,对原子类型变量的操作不可分割,因此,常用在多线程并发场景,避免出现竞态问题。

简单的使用示例:

use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::Arc;
use std::thread;fn main() {let atomic_counter = Arc::new(AtomicI32::new(0));let mut handles = vec![];/* 使用vec保存线程句柄,回收资源使用 */for _ in 0..5 {let counter = Arc::clone(&atomic_counter);let handle = thread::spawn(move || {for _ in 0..1_0 {counter.fetch_add(1, Ordering::SeqCst);}});handles.push(handle);}for handle in handles {handle.join().unwrap();}println!("Final value: {}", atomic_counter.load(Ordering::SeqCst));
}

创建了5个线程对原子变量进行递增10次,输出结果为50。若未使用原子变量且不加互斥保护,得到的结果是未知的。

boys@server:~/rust_study/demo$ cargo run Compiling demo v0.1.0 (/home/boys/rust_study/demo)Finished dev [unoptimized + debuginfo] target(s) in 0.39sRunning `target/debug/demo`
Final value: 50

相关文章:

Rust多线程编程

Rust多线程编程 文章目录 Rust多线程编程使用线程模块创建线程线程传参闭包&#xff08;匿名函数&#xff09;值捕获不可变引用捕获可变引用捕获 线程闭包传参更优雅地传参 回收线程线程同步和通信channel 通道mutex 互斥锁Barrier 栅栏Atomic Types 原子类型 使用线程模块 ru…...

什么是 TF-IDF 算法?

简单来说&#xff0c;向量空间模型就是希望把查询关键字和文档都表达成向量&#xff0c;然后利用向量之间的运算来进一步表达向量间的关系。比如&#xff0c;一个比较常用的运算就是计算查询关键字所对应的向量和文档所对应的向量之间的 “相关度”。 简单解释TF-IDF TF &…...

干货!耽误你1分钟,教你怎么查自己的流量卡是什么卡?

很多朋友都想购买一张正规的号卡&#xff0c;但是在网上一搜流量卡&#xff0c;五花八门&#xff0c;各式各样&#xff0c;那么&#xff0c;我们该如何辨别流量卡呢。 ​ 从种类上来看&#xff0c;网上的流量卡一共分为两种&#xff1a;号卡和物联卡 物联卡不用多说&#xff0…...

Spring Boot + Vue的网上商城实战入门

Spring Boot Vue的网上商城实战入门 技术栈调研 当使用Spring Boot和Vue构建网上商城项目时&#xff0c;以下是常用的技术栈和工具&#xff1a; 后端技术栈&#xff1a; Spring Boot&#xff1a;用于构建后端API&#xff0c;提供数据服务&#xff1b;Spring MVC&#xff1a;…...

云上办公系统项目

云上办公系统项目 1、云上办公系统1.1、介绍1.2、核心技术1.3、开发环境说明1.4、产品展示后台前台 1.5、 个人总结 2、后端环境搭建2.1、建库建表2.2、创建Maven项目pom文件guigu-oa-parentcommoncommon-utilservice-utilmodelservice-oa 配置数据源、服务器端口号application…...

three.js(九):内置的路径合成几何体

路径合成几何体 TubeGeometry 管道LatheGeometry 车削ExtrudeGeometry 挤压 TubeGeometry 管道 TubeGeometry(path : Curve, tubularSegments : Integer, radius : Float, radialSegments : Integer, closed : Boolean) path — Curve - 一个由基类Curve继承而来的3D路径。 De…...

【MySQL系列】索引的学习及理解

「前言」文章内容大致是MySQL索引的学习。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、索引概念二、从硬件角度理解2.1 磁盘2.2 结论 三、从软件角度理解四、共识五、索引的理解5.1 一个现象和结论5.2 对Page进行建模5.3 索引可以采用的数据结构5.…...

GPT-4.0技术大比拼:New Bing与ChatGPT,哪个更适合你

随着GPT-4.0技术的普及和发展&#xff0c;越来越多的平台开始将其应用于各种场景。New Bing已经成功接入GPT-4.0&#xff0c;并将其融入搜索和问答等功能。同样&#xff0c;在ChatGPT官网上&#xff0c;用户只需开通Plus账号&#xff0c;即可体验到GPT-4.0带来的智能交流和信息…...

vnc与windows之间的复制粘贴

【原创】VNC怎么和宿主机共享粘贴板 假设目标主机是linux&#xff0c;终端主机是windows&#xff08;就是在windows上使用VNC登陆linux&#xff09; 在linux中执行 vncconfig -nowin& 在linux选中文字后&#xff0c;无需其他按键&#xff0c;直接在windows中可以黏贴。 …...

windows下如何搭建属于自己的git服务器

前一阵子公司需要&#xff0c;领导让我给我们技术部搭建一个git服务器。以前看过教程&#xff0c;但自己没动手做过&#xff0c;开始按照网上的教程来&#xff0c;但搭建过程中发现还是不够详细&#xff0c;今天给大家一个比较详细的&#xff0c;希望对大家有帮助。 高能预警&a…...

D360周赛复盘:模拟(思维题目)⭐⭐+贪心解决可能的最小和(类似上次)

文章目录 2833.距离原点最远的点思路完整版 2834.找出美丽数组的最小和思路完整版 2833.距离原点最远的点 给你一个长度为 n 的字符串 moves &#xff0c;该字符串仅由字符 L、R 和 _ 组成。字符串表示你在一条原点为 0 的数轴上的若干次移动。 你的初始位置就在原点&#xf…...

【C++学习】函数指针

#include<iostream> //包含头文件 using namespace std; void func(int no, string str){cout << "亲爱的"<< no << "号:" << str << endl; }int main(){int bh 3;string message "我是一只傻傻鸟";func…...

A. Copil Copac Draws Trees

Problem - 1830A - Codeforces 问题描述&#xff1a; 科皮尔-科帕克&#xff08;Copil Copac&#xff09;得到一个由 n − 1 n-1 n−1条边组成的列表&#xff0c;该列表描述了一棵由 n n n个顶点组成的树。他决定用下面的算法来绘制它&#xff1a; 步骤 0 0 0&#xff1a…...

D359周赛复盘:贪心解决求最小和问题⭐⭐+较为复杂的双层线性DP⭐⭐

文章目录 2828.判别首字母缩略词完整版 2829.k-avoiding数组的最小总和&#xff08;贪心解法&#xff09;思路完整版 类似题&#xff1a;2834.找出美丽数组的最小和思路完整版 2830.销售利润最大化⭐⭐思路DP数组含义递推公式 完整版 2828.判别首字母缩略词 给你一个字符串数组…...

python基础之miniConda管理器

一、介绍 MiniConda 是一个轻量级的 Conda 版本&#xff0c;它是 Conda 的精简版&#xff0c;专注于提供基本的环境管理功能。Conda 是一个流行的开源包管理系统和环境管理器&#xff0c;用于在不同的操作系统上安装、管理和运行软件包。 与完整版的 Anaconda 相比&#xff0c…...

C++算法 —— 分治(1)快排

文章目录 1、颜色分类2、排序数组3、第k个最大的元素&#xff08;快速选择&#xff09;4、最小的k个数&#xff08;快速选择&#xff09; 分治&#xff0c;就是分而治之&#xff0c;把大问题划分成多个小问题&#xff0c;小问题再划分成更小的问题。像快排和归并排序就是分治思…...

接口用例设计

章节目录&#xff1a; 一、针对输入设计1.1 数值型1.2 字符串型1.3 数组或链表类型 二、针对业务逻辑2.1 约束条件分析2.2 操作对象分析2.3 状态转换分析2.4 时序分析 三、针对输出设计3.1 针对输出结果3.2 接口超时 四 、其他测试设计4.1 已废弃接口测试4.2 接口设计合理性分析…...

Selenium超级详细的教程

Selenium是一个用于自动化测试的工具&#xff0c;它可以模拟用户在浏览器中的各种操作。除了用于测试&#xff0c;Selenium还可以用于爬虫&#xff0c;特别是在处理动态加载页面时非常有用。本文将为您提供一个超级详细的Selenium教程&#xff0c;以帮助您快速入门并了解其各种…...

服务报network error错误

问题&#xff1a;服务请求时会偶发性的报【network error网络超时】&#xff08;请求瞬间就报&#xff09; 可能原因&#xff1a; 服务器linux内核调优时将&#xff1a;net.ipv4.tcp_tw_recycle设置为1&#xff0c;开启TCP连接中TIME-WAIT sockets的快速回收&#xff0c;默认为…...

【ES6】利用 Proxy实现函数名链式效果

利用 Proxy&#xff0c;可以将读取属性的操作&#xff08;get&#xff09;&#xff0c;转变为执行某个函数&#xff0c;从而实现属性的链式操作。 var pipe function (value) {var funcStack [];var oproxy new Proxy({} , {get : function (pipeObject, fnName) {if (fnNa…...

hive部署

下载hive安装包&#xff1a;https://dlcdn.apache.org/hive/hive-2.3.9/解压及环境部署 tar -zxvf apache-hive-2.3.9-bin.tar.gz mv apache-hive-2.3.9-bin hivevim /etc/profile添加至环境变量 export HIVE_HOME/usr/local/hive export PATH$PATH:$HIVE_HOME/binsource /etc…...

ip白名单之网段

代码托管&#xff0c;有时候为了安全性&#xff0c;限制网段内的ip可以访问。 IP地址和掩码均知道时才能确定主机所在的网段&#xff0c;也就是用这个原理来限制可访问的IP网段了。 ip后面加上“/N”就代表掩码的二进制”1“有N位。 例如&#xff1a; ①0.0.0.0/0 主机ip地…...

PMP项目管理主要学习内容是什么?

PMP项目管理是指根据美国项目管理学会(Project Management Institute&#xff0c;简称PMI)制定的项目管理知识体系和方法论进行项目管理的一种认证。PMP主要关注项目的规划、执行和控制等方面的知识和技能。 下面是PMP项目管理《PMBOK指南》第六版的主要学习内容&#xff1a; …...

小米面试题——不用加减乘除计算两数之和

前言 &#xff08;1&#xff09;刷B站看到一个面试题&#xff0c;不用加减乘除计算两数之和。 &#xff08;2&#xff09;当时我看到这个题目&#xff0c;第一反应就是感觉这是一个数电题目。不过需要采用C语言的方式编写出来。 &#xff08;3&#xff09;不过看到大佬的代码之…...

Mysql 日志管理 数据备份

MySQL日志管理 MySQL的默认日志保存位置为/usr/local/mysql/data 日志开启方式有两种&#xff1a;通过配置文件或者是通过命令 通过命令修改开启的日志是临时的&#xff0c;关闭或重启服务后就会关闭 日志的分类 1.错误日志 用来记录当MySQL启动、停止或运行时发生的错误信…...

Java小记-腾讯2020校招-后台-逛街

题目描述&#xff1a; 小Q在周末的时候和他的小伙伴来到大城市逛街&#xff0c;一条步行街上有很多高楼&#xff0c;共有n座高楼排成一行。 小Q从第一栋一直走到了最后一栋&#xff0c;小Q从来都没有见到这么多的楼&#xff0c;所以他想知道他在每栋楼的位置处能看到多少栋楼呢…...

FFmpeg5.0源码阅读——FFmpeg大体框架

摘要&#xff1a;前一段时间熟悉了下FFmpeg主流程源码实现&#xff0c;对FFmpeg的整体框架有了个大概的认识&#xff0c;因此在此做一个笔记&#xff0c;希望以比较容易理解的文字描述FFmpeg本身的结构&#xff0c;加深对FFmpeg的框架进行梳理加深理解&#xff0c;如果文章中有…...

【算法刷题之字符串篇】

目录 1.leetcode-344. 反转字符串&#xff08;1&#xff09;方法&#xff1a;双指针 2.leetcode-541. 反转字符串 II&#xff08;1&#xff09;方法一&#xff1a;模拟&#xff08;2&#xff09;方法二&#xff1a;双指针 3.leetcode-剑指 Offer 05. 替换空格&#xff08;1&…...

js中forEach和map的区别:forEach不会改变原数组,而map会改变数组?错了错了

1.提出思考&#xff1f;forEach不会改变原数组&#xff0c;而map会改变数组&#xff1f; 看到掘金上一篇文章觉得很有意思&#xff1a;大致是描述一般面试官问js中forEach和map的区别&#xff1f;都会回答forEach不会改变原数组&#xff0c;而map会改变&#xff0c;我也一直对…...

深度对话:从底层看Sui设计理念及网络规模扩展

近日&#xff0c;我们采访了George Danezis以了解Sui的交易处理系统如何促成高性能网络。他是Mysten Labs的联合创始人和首席科学家&#xff08;Sui的最初贡献者&#xff09;&#xff0c;也是伦敦大学学院&#xff08;University College London&#xff0c;UCL&#xff09;安全…...