Rust循环引用与多线程并发

循环引用与自引用

循环引用的概念

循环引用指的是两个或多个对象之间相互持有对方的引用。在 Rust 中,由于所有权和生命周期的严格约束,直接创建循环引用通常会导致编译失败。例如:

// 错误的循环引用示例
struct Node {
    next: Option<Box<Node>>,
}

fn create_cycle() {
    let n1 = Box::new(Node { next: None });
    let n2 = Box::new(Node { next: Some(n1) }); // 编译错误
    n1.next = Some(n2); // 编译错误
}

在这个例子中,尝试创建一个简单的双向链表,但由于所有权转移问题,编译器会报错。

自引用结构体的实现

自引用结构体是指一个结构体内部包含对自身实例的引用。这种结构常用于实现树形数据结构或其他需要递归引用的场景。

use std::rc::{Rc, Weak};

struct Node {
    value: i32,
    parent: Option<Weak<Rc<Node>>>,
    children: Vec<Rc<Node>>,
}

impl Node {
    fn new(value: i32) -> Self {
        Node {
            value,
            parent: None,
            children: Vec::new(),
        }
    }

    fn add_child(&mut self, child: Rc<Node>) {
        self.children.push(child.clone());
        child.parent = Some(Rc::downgrade(&self));
    }
}

使用 Rc 和 Weak 解决循环引用

为了处理循环引用问题,Rust 提供了 Rc 和 Weak 两种类型:

  • Rc<T>: 引用计数类型,允许多个所有者。
  • Weak<T>: 对应于 Rc<T> 的弱引用版本,不会增加引用计数。

通过使用 Weak 可以打破循环引用,因为 Weak 不会增加其指向的对象的引用计数。

生命周期注解的应用

在 Rust 中,生命周期注解可以帮助编译器更好地理解引用之间的关系。特别是在自引用和循环引用的情况下,生命周期注解尤为重要。

// 定义一个带有生命周期注解的函数
fn process_node<'a>(node: &'a Node) {
    println!("Processing node with value: {}", node.value);

    // 访问子节点
    for child in &node.children {
        process_node(child); // 递归处理子节点
    }
}

// 使用生命周期注解的结构体方法
impl<'a> Node {
    fn traverse<'b>(&'a self, visitor: &dyn Fn(&'b Node)) {
        visitor(self);
        for child in &self.children {
            child.traverse(visitor);
        }
    }
}

实际代码示例与分析

下面是一个完整的示例,展示了如何创建并操作自引用结构体:

use std::rc::{Rc, Weak};

struct Node {
    value: i32,
    parent: Option<Weak<Rc<Node>>>,
    children: Vec<Rc<Node>>,
}

impl Node {
    fn new(value: i32) -> Self {
        Node {
            value,
            parent: None,
            children: Vec::new(),
        }
    }

    fn add_child(&mut self, child: Rc<Node>) {
        self.children.push(child.clone());
        child.parent = Some(Rc::downgrade(&self));
    }
}

fn main() {
    let root = Rc::new(Node::new(0));
    let child1 = Rc::new(Node::new(1));
    let child2 = Rc::new(Node::new(2));

    root.add_child(child1.clone());
    root.add_child(child2.clone());

    println!("Root has {} children", root.children.len());

    // 访问子节点的父节点
    if let Some(parent) = child1.parent {
        if let Some(p) = parent.upgrade() {
            println!("Child 1's parent is {}", p.value);
        }
    }

    // 遍历树结构
    root.traverse(&|node| println!("Visiting node with value: {}", node.value));
}

定义 Node 结构:

  • value: 节点存储的值。
  • parent: 父节点的弱引用,初始为 None。
  • children: 一个向量,存储子节点的强引用。

创建新节点:

  • new 方法初始化一个新的 Node 实例,此时没有父节点也没有子节点。

添加子节点:

  • add_child 方法接收一个 Rc<Node> 类型的参数作为子节点。
  • 将子节点添加到当前节点的 children 向量中。
  • 更新子节点的 parent 字段,使用 Rc::downgrade 转换为 Weak 引用。

遍历树结构:

  • traverse 方法使用生命周期注解,递归地遍历整个树结构。

多线程并发

并发与并行概述

  • 并发 (Concurrency): 多个任务可以在同一时间间隔内执行,但不一定在同一时刻执行。
  • 并行 (Parallelism): 多个任务在同一时刻执行,通常涉及硬件支持。

在 Rust 中,可以通过多线程实现并发,而并行则依赖于多核处理器的支持。

使用多线程

在 Rust 中,可以使用标准库中的 std::thread 模块来创建和管理线程。

创建线程

use std::thread;
use std::time::Duration;

fn spawn_thread() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("Thread spawned: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("Main thread: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}

fn main() {
    spawn_thread();
}

线程同步:消息传递

在 Rust 中,消息传递是一种常见的线程间通信方式。常用的工具包括 std::sync::mpsc 模块中的通道 (channel)。

使用通道

use std::sync::mpsc;
use std::thread;

fn send_messages() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("Hello from the other side!");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

fn main() {
    send_messages();
}

线程同步:锁

Rust 标准库提供了多种锁机制,如 Mutex、RwLock 和 Arc。

使用 Mutex

use std::sync::Mutex;
use std::thread;

fn lock_data() {
    let counter = Mutex::new(0);

    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Mutex::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!("Counter: {}", *counter.lock().unwrap());
}

fn main() {
    lock_data();
}

使用 RwLock

use std::sync::RwLock;
use std::thread;

fn read_write_lock() {
    let data = RwLock::new(String::from("Hello"));

    let mut handles = vec![];

    for _ in 0..10 {
        let data = RwLock::clone(&data);
        let handle = thread::spawn(move || {
            let mut d = data.write().unwrap();
            *d += "!";
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Data: {}", *data.read().unwrap());
}

fn main() {
    read_write_lock();
}

线程同步:条件变量和信号量

Rust 标准库提供了 Condvar 和 Semaphore 等高级同步原语。

使用 Condvar

use std::sync::{Arc, Condvar, Mutex};
use std::thread;

fn condition_variable() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair_clone = Arc::clone(&pair);

    thread::spawn(move || {
        let (lock, cvar) = &*pair;
        let mut started = lock.lock().unwrap();
        *started = true;
        cvar.notify_one();
    });

    let (lock, cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }

    println!("Condition variable signaled!");
}

fn main() {
    condition_variable();
}

使用 Semaphore

use std::sync::Semaphore;
use std::thread;

fn semaphore_example() {
    let sem = Semaphore::new(3);

    let mut handles = vec![];

    for _ in 0..5 {
        let sem = sem.clone();
        let handle = thread::spawn(move || {
            sem.acquire().unwrap();
            println!("Acquired semaphore");
            thread::sleep(std::time::Duration::from_secs(1));
            sem.release();
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

fn main() {
    semaphore_example();
}

线程同步:原子操作与内存顺序

Rust 标准库提供了 std::sync::atomic 模块,用于原子操作和内存顺序控制。

原子操作

use std::sync::atomic::{AtomicUsize, Ordering};

fn atomic_operations() {
    let counter = AtomicUsize::new(0);

    let mut handles = vec![];

    for _ in 0..10 {
        let counter = counter.clone();
        let handle = thread::spawn(move || {
            counter.fetch_add(1, Ordering::Relaxed);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Counter: {}", counter.load(Ordering::Relaxed));
}

fn main() {
    atomic_operations();
}

内存顺序

use std::sync::atomic::{AtomicUsize, Ordering};

fn memory_ordering() {
    let flag = AtomicUsize::new(0);
    let data = AtomicUsize::new(0);

    let mut handles = vec![];

    let flag_clone = flag.clone();
    let data_clone = data.clone();
    let handle1 = thread::spawn(move || {
        flag_clone.store(1, Ordering::Release);
        data_clone.store(42, Ordering::Relaxed);
    });

    let flag_clone = flag.clone();
    let data_clone = data.clone();
    let handle2 = thread::spawn(move || {
        while flag_clone.load(Ordering::Acquire) == 0 {}
        assert_eq!(data_clone.load(Ordering::Relaxed), 42);
    });

    handles.push(handle1);
    handles.push(handle2);

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Memory ordering example completed.");
}

fn main() {
    memory_ordering();
}

基于 Send 和 Sync 的线程安全

在 Rust 中,Send 和 Sync 是两个重要的类型约束,用于确保数据在线程间安全传递。

Send 约束

use std::thread;

fn send_constraint() {
    struct NotSend(u8);

    impl NotSend {
        fn new() -> Self {
            NotSend(0)
        }
    }

    // NotSend 类型不能在线程间传递
    // let handle = thread::spawn(move || {
    //     println!("NotSend value: {}", NotSend::new().0);
    // });

    // 正确的示例
    let handle = thread::spawn(|| {
        println!("Send value: {}", 42);
    });

    handle.join().unwrap();
}

fn main() {
    send_constraint();
}

Sync 约束

use std::sync::Arc;
use std::thread;

fn sync_constraint() {
    struct NotSync(u8);

    impl NotSync {
        fn new() -> Self {
            NotSync(0)
        }
    }

    // NotSync 类型不能在线程间共享
    // let shared = NotSync::new();
    // let handle = thread::spawn(move || {
    //     println!("NotSync value: {}", shared.0);
    // });

    // 正确的示例
    let shared = Arc::new(42);
    let handle = thread::spawn(move || {
        println!("Sync value: {}", shared);
    });

    handle.join().unwrap();
}

fn main() {
    sync_constraint();
}

文章到此结束,更多相关的信息,请,https://t.me/gtokentool
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL面试50题

数据表关系图 数据表 CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) NOT NULL,sex enum(female,male) NOT NULL,birth date NOT NULL,credit float(5,2) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT25 DEFAULT CHARSETutf8;…

Redis(配置文件属性解析)

一、tcp-backlog深度解析 tcp-backlog是一个TCP连接的队列&#xff0c;主要用于解决高并发场景下客户端慢连接问题。配置文件中的“511”就是队列的长度&#xff0c;对联与TCP的三次握手有关&#xff0c;不同的linux内核&#xff0c;backlog队列中存放的元素&#xff08;客户端…

Vue3 脚手架扩展

当 yarn dev 运行成功后&#xff0c;我们继续添加扩展 首先我们要安装一些依赖 其中的vue-router和vuex安装最新版的就行&#xff0c;因为项目是vue3 element-plus和less&#xff0c;less-loader最好按照我这个版本来下载 element-plus是一个vue常用的ui组件库 element-plus/…

TCP/IP协议簇自学笔记

摘抄于大学期间记录在QQ空间的一篇自学笔记&#xff0c;当前清理空间&#xff0c;本来想直接删除掉的&#xff0c;但是感觉有些舍不得&#xff0c;因此先搬移过来。 曾经&#xff0c;我只知道socket函数能进行网络间数据的通信&#xff0c;知道tcp/ip协议也是用来进行网络数据…

AI开发:逻辑回归 - 实战演练- 垃圾邮件的识别(二)

接上一篇AI开发&#xff1a;逻辑回归 - 实战演练- 垃圾邮件的识别&#xff08;一&#xff09; new_email 无论为什么文本&#xff0c;识别结果几乎都是垃圾邮件,因此我们需要对源码的逻辑进行梳理一下&#xff1a; 在代码中&#xff0c;new_email 无论赋值为何内容都被识别为…

字符串处理(二)

第1题 篮球比赛 查看测评数据信息 学校举行篮球比赛&#xff0c;请设计一个计分系统统计KIN、WIN两队分数&#xff0c;并输出分数和结果&#xff01; 如果平分就输出‘GOOD’&#xff0c;否则输出获胜队名&#xff01; 输入格式 输入数据共n1行&#xff0c; 第1行n&#xf…

【数据库系列】Liquibase 与 Flyway 的详细对比

在现代软件开发中&#xff0c;数据库版本控制是一个至关重要的环节。为了解决数据库迁移和变更管理的问题&#xff0c;开发者们通常会使用工具&#xff0c;如 Liquibase 和 Flyway。本文将对这两个流行的数据库迁移工具进行详细比较&#xff0c;从基础概念、原理、优缺点到使用…

企业品牌曝光的新策略:短视频矩阵系统

企业品牌曝光的新策略&#xff1a;短视频矩阵系统 在当今数字化时代&#xff0c;短视频已经渗透到我们的日常生活之中&#xff0c;成为连接品牌与消费者的关键渠道。然而&#xff0c;随着平台于7月20日全面下线了短视频矩阵的官方接口&#xff0c;许多依赖于此接口的小公司和内…

PostgreSQL最常用数据类型-重点说明自增主键处理

简介 PostgreSQL提供了非常丰富的数据类型&#xff0c;我们平常使用最多的基本就3类&#xff1a; 数字类型字符类型时间类型 这篇文章重点介绍这3中类型&#xff0c;因为对于高并发项目还是推荐&#xff1a;尽量使用简单类型&#xff0c;把运算和逻辑放在应用中&#xff0c;…

做异端中的异端 -- Emacs裸奔之路4: 你不需要IDE

确切地说&#xff0c;你不需要在IDE里面编写或者阅读代码。 IDE用于Render资源文件比较合适&#xff0c;但处理文本&#xff0c;并不划算。 这的文本文件&#xff0c;包括源代码&#xff0c;配置文件&#xff0c;文档等非二进制文件。 先说说IDE带的便利: 函数或者变量的自动…

ospf协议(动态路由协议)

ospf基本概念 定义 OSPF 是典型的链路状态路由协议&#xff0c;是目前业内使用非常广泛的 IGP 协议之一。 目前针对 IPv4 协议使用的是 OSPF Version 2 &#xff08; RFC2328 &#xff09;&#xff1b;针对 IPv6 协议使用 OSPF Version 3 &#xff08; RFC2740 &#xff09;。…

【热门主题】000072 分布式数据库:开启数据管理新纪元

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【热…

Python 3 教程第33篇(MySQL - mysql-connector 驱动)

Python MySQL - mysql-connector 驱动 MySQL 是最流行的关系型数据库管理系统&#xff0c;如果你不熟悉 MySQL&#xff0c;可以阅读我们的 MySQL 教程。 本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL&#xff0c; mysql-connector 是 MySQL 官方提供的驱动器。…

ENSP IPV6-over-IPV4

IPv6是网络层协议的第二代标准协议&#xff0c;一个IPv6地址同样可以分为网络前缀和主机ID两个部分。 可以将IPV4的网络看成IPV6的承载网&#xff0c;只有IPv4网络是连通的&#xff0c;则IPv6网络才有可能连通。所以配置的时候需要先配置IPv4网络的路由功能&#xff0c;再配IP…

《数据挖掘:概念、模型、方法与算法(第三版)》

嘿&#xff0c;数据挖掘的小伙伴们&#xff01;今天我要给你们介绍一本超级实用的书——《数据挖掘&#xff1a;概念、模型、方法与算法》第三版。这本书是数据挖掘领域的经典之作&#xff0c;由该领域的知名专家编写&#xff0c;系统性地介绍了在高维数据空间中分析和提取大量…

53 基于单片机的8路抢答器加记分

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 首先有三个按键 分别为开始 暂停 复位&#xff0c;然后八个选手按键&#xff0c;开机显示四条杠&#xff0c;然后按一号选手按键&#xff0c;数码管显示&#xff13;&#xff10;&#xff0c;这…

从零开始写游戏之斗地主-网络通信

在确定了数据结构后&#xff0c;原本是打算直接开始写斗地主的游戏运行逻辑的。但是突然想到我本地写出来之后&#xff0c;也测试不了啊&#xff0c;所以还是先写通信模块了。 基本框架 在Java语言中搞网络通信&#xff0c;那么就得请出Netty这个老演员了。 主要分为两个端&…

Logistic Regression(逻辑回归)、Maximum Likelihood Estimatio(最大似然估计)

Logistic Regression&#xff08;逻辑回归&#xff09;、Maximum Likelihood Estimatio&#xff08;最大似然估计&#xff09; 逻辑回归&#xff08;Logistic Regression&#xff0c;LR&#xff09;逻辑回归的基本思想逻辑回归模型逻辑回归的目标最大似然估计优化方法 逻辑回归…

数据类型.

数据类型分类 数值类型 tinyint类型 以tinyint为例所有数值类型默认都是有符号的&#xff0c;无符号的需要在后面加unsignedtinyint的范围在-128~127之间无符号的范围在0~255之间(类比char) create database test_db; use test_db;建表时一定要跟着写上属性 mysql> creat…

IDEA使用HotSwapHelper进行热部署

目录 前言JDK1.8特殊准备DECVM安装插件安装与配置参考文档相关下载 前言 碰到了一个项目&#xff0c;用jrebel启动项目时一直报错&#xff0c;不用jrebel时又没问题&#xff0c;找不到原因&#xff0c;又不想放弃热部署功能 因此思考能否通过其他方式进行热部署&#xff0c;找…