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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/58915.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud + Spring Boot 项目搭建结构层次示例讲解

Spring Cloud Spring Boot 项目搭建结构层次示例讲解 Spring Cloud 项目搭建结构层次示例Spring Cloud示例&#xff1a; Spring Boot 项目搭建结构层次讲解Spring Boot 项目通常按照一种常见的架构模式组织&#xff0c;可以分为以下几个主要层次&#xff1a;当构建一个 Spring…

【算法系列篇】位运算

文章目录 前言什么是位运算算法1.判断字符是否唯一1.1 题目要求1.2 做题思路1.3 Java代码实现 2. 丢失的数字2.1 题目要求2.2 做题思路2.3 Java代码实现 3. 两数之和3.1 题目要求3.2 做题思路3.3 Java代码实现 4. 只出现一次的数字4.1 题目要求4.2 做题思路4.3 Java代码实现 5.…

Ansible项目实战管理/了解项目环境/项目管理

一&#xff0c;项目环境 1.项目基础 项目过程 调研阶段 设计阶段 开发阶段 测试阶段 运营阶段 2.项目环境 个人开发环境 公司开发环境 项目测试环境 项目预发布环境 灰度环境&#xff1a;本身是生产环境&#xff0c;安装项目规划&#xff0c;最终所有的生产环境都发…

React——组件缓存 react-activation

1、安装依赖 npm i -S react-activation 2、包裹根组件 import { AliveScope } from "react-activation"<AliveScope><App /> </AliveScope> 3、缓存组件 import { KeepAlive } from "react-activation"export default () > {co…

Python框架【模板继承 、继承模板实战、类视图 、类视图的好处 、类视图使用场景、基于调度方法的类视图】(四)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

CUDA小白 - NPP(2) -图像处理-算数和逻辑操作(2)

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…

解压jar包并导入库环境

背景 因为各种历史原因&#xff0c;当初的maven依赖环境已下载不了了&#xff0c;所以需要从生产环境的jar包里&#xff0c;获取库环境来本地运行。 但是网上很多方法都是用mvn install命令&#xff0c;一个个jar包导入的&#xff0c;不符合我的需求&#xff08;需要导入280多…

R语言APRIORI关联规则、K-MEANS均值聚类分析中药专利复方治疗用药规律网络可视化...

全文链接&#xff1a;http://tecdat.cn/?p30605 应用关联规则、聚类方法等数据挖掘技术分析治疗的中药专利复方组方配伍规律&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 方法检索治疗中药专利复方&#xff0c;排除外用中药及中西药物合用的复方。最近我们…

CSS3D+动画

CSS3D 1.css3D 给父元素设置 perspective:景深:近大远小的效果900-1200px这个范围内 transform-style:是否设置3D环境 flat 2D环境 默认值 perserve-3D环境 3D功能函数 1.位移: translateZ()translate3D(x,y,z) <!DOCTYPE html> <html lang"en"><h…

linux下载安装jdk

1.java安装unzip,zip yum install -y unzip zip 2.安装vim yum -y install vim* Linux-jdk1.8下载地址点击下载 jdk1.8-linux 提取码&#xff1a;h40h 1、将安装包上传至Linux服务器 2、创建安装文件夹 创建install mkdir -p /export/install3、解压到install文件夹中 tar…

13.搬砖

目录 题目 Description Input Output 思路&#xff08;归并排序&#xff09; 具体步骤如下 C整体代码&#xff08;含详细注释&#xff09; 归并排序总结 核心步骤 代码模板 题目 Description 小张在暑假时间来到工地搬砖挣钱。包工头交给他一项艰巨的任务&#xff0…

【axios网络请求库】认识Axios库;axios发送请求、创建实例、创建拦截器、封装请求

目录 1_认识Axios库2_axios发送请求3_axios创建实例4_axios的拦截器5_axios请求封装 1_认识Axios库 功能特点: 在浏览器中发送 XMLHttpRequests 请求在 node.js 中发送 http请求支持 Promise API拦截请求和响应转换请求和响应数据 2_axios发送请求 支持多种请求方式: axios…

Linux环境下搭建使用缓存中间件Redis

缓存中间件Redis搭建与使用 前言正文1 提供安装环境2 下载安装3 修改启动配置4 启动服务5 使用6 关闭服务7 卸载 前言 redis服务将在linux系统中部署&#xff0c;本文前提是已经搭建一个linux系统&#xff0c;并配置好网络等。使用vmware搭建一个linux系统&#xff0c;可以参考…

Coal Wall Creation And Code Optimization

Code: using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using ZongCaiGongZuoMian;public class CoalWall : MonoBehaviour {[SerializeField]Material mat;float zjInternal;//支架间隔float zjHeightMax;Transf…

Royal TSX 6 Mac多协议远程软件

Royal TSX是一款功能强大的远程桌面管理软件&#xff0c;适用于Mac操作系统。它允许用户通过一个集成的界面来管理和访问多个远程计算机和服务器。 Royal TSX支持多种远程协议&#xff0c;包括RDP、VNC、SSH、Telnet和FTP等&#xff0c;可以方便地连接到Windows、Linux、Mac和其…

非煤矿山风险监测预警算法 yolov8

非煤矿山风险监测预警算法通过yolov8网络模型深度学习算法框架&#xff0c;非煤矿山风险监测预警算法在煤矿关键地点安装摄像机等设备利用智能化视频识别技术&#xff0c;能够实时分析人员出入井口的情况&#xff0c;人数变化并检测作业状态。YOLO的结构非常简单&#xff0c;就…

ios开发 swift5 苹果系统自带的图标 SF Symbols

文章目录 1.官网app的下载和使用2.使用代码 1.官网app的下载和使用 苹果官网网址&#xff1a;SF Symbols 通过上面的网址可以下载dmg, 安装到自己的mac上 貌似下面这样不能展示出动画&#xff0c;还是要使用动画的代码 .bounce.up.byLayer2.使用代码 UIKit UIImage(system…

解决报错Java: 非法字符: ‘\ufeff‘

方法一&#xff1a;直接remove BOM&#xff0c;再重新启动程序。 方法二&#xff1a;用notpad打开&#xff0c;点击编码为utf-8格式&#xff0c;保存。

设置好以下几点Idea配置,极大提升研发效率

这里分享几点配置IntelliJ IDEA的建议,可以提升开发效率: 1.自定义代码风格(Code Style),统一项目代码风格 在IntelliJ IDEA中可以自定义代码风格,主要包括: 设置代码的缩进规则,如:缩进空格数,tab是否使用空格代替等。 在Settings - Editor - Code Style - Java中配置。 …

DockerFile常用命令

以下是常见的Dockerfile命令&#xff1a; FROM&#xff1a;FROM命令用于指定基础镜像。基础镜像是构建镜像的起点。例如&#xff0c;FROM ubuntu:latest表示使用最新版本的Ubuntu作为基础镜像。 MAINTAINER&#xff1a;MAINTAINER命令用于指定镜像的维护者信息。一般格式为&am…