无畏并发: Rust Mutex的基本使用

news2024/11/16 15:56:45

并发是很多编程语言避不开的一块主要内容,主打一个无畏并发的Rust自然也面临这样的挑战。Rust中的Mutex提供了强大的同步原语,确保共享数据的线程安全,这篇文章中,我们会探讨Mutex的使用,从基础的用法到一些高阶内容。
在这里插入图片描述


1. Rust中的互斥锁

Mutex作为Rust中的一种互斥锁,它一次只允许一个线程访问共享资源。

  • 提供了一种同步可变数据访问的机制,防止数据竞争、确保线程安全;

  • 位于Rust标准库std::sync::Mutex中,支持对共享可变状态的安全并发访问。

  • 互斥锁允许一个线程在访问共享数据时获取锁,而其他线程必须等待该线程释放锁后才能访问共享数据,从而确保数据的安全性和一致性。


2. 基本用法体验

下面以使用Mutex保护共享数据为例演示Mutex的基础用法。

use std::{sync::Mutex, thread};
fn main() {
    let counter = Mutex::new(0);

    let handle = thread::spawn(move || {
        let mut value = counter.lock().unwrap();
        *value += 1;
    });

    handle.join().expect("thread panicked!");

    let value = counter.lock().unwrap();
    println!("Counter:{}", *value);
}

这段代码演示了Mutex的基本用法,但它是无法通过编译的,尽管如此,这里还是对上述代码进行解释一下:

  1. let counter = Mutex::new(0);

    • 在这行代码中,创建了一个名为 counterMutex,其中包含一个整数值初始为 0。这个 Mutex 将用于保护共享的整数值,以确保线程安全访问。
  2. let handle = thread::spawn(move || { ... });

    • 使用 thread::spawn 创建一个新线程,并在该线程中执行一个闭包。这个闭包中的内容将在新线程中运行。
    • move 关键字用于将 counter 的所有权转移给闭包,确保闭包可以在新线程中使用 counter
  3. let mut val = counter.lock().unwrap();

    • 在闭包中,通过调用 lock() 方法获取 counter 的锁,并使用 unwrap() 处理可能的锁获取失败的情况。这里的 val 是一个 MutexGuard,它允许我们安全地访问被 Mutex 保护的数据。
  4. *val += 1;

    • 在获取了 counter 的锁之后,对共享数据进行递增操作。
  5. handle.join().expect("thread panicked!");

    • 等待新线程执行完毕。join() 方法会阻塞当前线程,直到新线程执行完毕。
    • 如果新线程发生了 panic,expect() 方法会打印指定的错误信息。
  6. let val = counter.lock().unwrap();

    • 在主线程中,再次获取 counter 的锁,以确保安全地访问共享数据。
  7. println!("Counter:{}", *val);

    • 打印最终的共享数据值。

那么问题来了,为什么无法通过编译:

  • 在这段代码在编译阶段会出现 borrow of moved value 的错误,因为我们在 thread::spawn 的闭包中移动了 counter,而后又在闭包外部尝试再次使用它。

  • 为了解决这个问题,我们需要使用 Arc (原子引用计数) 来在多线程之间安全地共享 counter

use std::{
    sync::{Arc, Mutex},
    thread,
};
fn main() {
    let counter = Arc::new(Mutex::new(0));

    let counter_clone = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut value = counter_clone.lock().unwrap();
        *value += 1;
    });

    match handle.join() {
        Ok(_) => {
            let value = counter.lock().unwrap();
            println!("Counter: {}", *value);
        }
        Err(_) => {
            println!("Thread panicked!");
        }
    }
}

修复之后的代码中,使用了Arcmutex进行了调整,通过使用 Arc 来创建一个引用计数的 Mutex,并在闭包中使用 Arc 的克隆。这样可以确保在多线程环境中安全地共享 counter,避免了 borrow of moved value 的问题。同时使用match表达式进一步对错误进行了处理。


3. 多个锁的使用

有时,我们可能需要独立保护多个共享资源。 Rust 允许您使用多个锁来实现这一点:

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

fn main() {
    let cnt1 = Arc::new(Mutex::new(0));
    let cnt2 = Arc::new(Mutex::new(0));

    let cnt1_clone = Arc::clone(&cnt1);
    let handle1 = thread::spawn(move || {
        let mut value = cnt1_clone.lock().unwrap();
        *value += 1;
    });

    let cnt2_clone = Arc::clone(&cnt2);
    let handle2 = thread::spawn(move || {
        let mut value2 = cnt2_clone.lock().unwrap();
        *value2 += 1;
    });

    match handle1.join() {
        Ok(_) => {
            let value1 = cnt1.lock().unwrap();
            println!("cnt1:{}", *value1);
        }
        Err(_) => {
            println!("Thread panicked!");
        }
    }

    match handle2.join() {
        Ok(_) => {
            let value2 = cnt2.lock().unwrap();
            println!("cnt2:{}", *value2);
        }
        Err(_) => {
            println!("Thread panicked!");
        }
    }
}

这段代码演示了如何使用 ArcMutex 在 Rust 中实现多线程并发访问共享数据:

  1. 首先,通过 use 关键字引入了需要使用的标准库中的一些模块,包括 ArcMutexthread

  2. main 函数中,创建了两个计数器 cnt1cnt2,它们分别被包装在 ArcMutex 中。这样做是为了确保在多线程环境中安全地访问这两个计数器。

  3. 使用 Arc::clone(&cnt1)Arc::clone(&cnt2) 创建了两个 Arc 的克隆,分别赋值给 cnt1_clonecnt2_clone。这样做是为了将计数器的所有权移动到新的线程中。

  4. 通过 thread::spawn 创建了两个线程 handle1handle2,分别对 cnt1_clonecnt2_clone 所指向的计数器进行操作。在每个线程中,首先获取了计数器的锁,然后对计数器的值进行加一操作。

  5. 使用 handle1.join()handle2.join() 分别等待两个线程的执行结果。如果线程执行成功,就获取相应计数器的锁并打印计数器的值;如果线程执行失败(比如发生了 panic),则打印出相应的错误信息。


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

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

相关文章

基于地理坐标的高阶几何编辑工具算法(8)——整形面

文章目录 工具步骤应用场景算法输入算法输出算法示意图算法原理工具步骤 选中面,点击“整形面”工具,绘制一条和面至少两个交点的线,双击结束。 应用场景 快速修改一个几何面的局部形状。 算法输入 一个待修改的面p,和一条绘制的线l 算法输出 修改后的面 算法示意图…

【JAVA基础之网络编程】UDP和TCP协议以及三次握手和四次挥手的过程

🔥作者主页:小林同学的学习笔录 🔥mysql专栏:小林同学的专栏 目录 1. 网络编程 1.1 概述 1.2 网络编程的三要素 1.2.1 IP地址 1.2.2 InetAddress 1.2.3 端口和协议 1.3 UDP协议 1.3.1 UDP发送数据 1.3.2 UDP接收数据 1.4…

Hadoop伪分布式搭建

1 配置SSH免密登录 1.生成密钥 # ssh-keygen -t rsa 注意:需要经过4次回车 查看密钥及公钥 # cd /root/.ssh 拷贝公钥 # cp id_rsa.pub authorized_keys 2 测试本地免密登录 2 下载Hadoop安装包 使用wget命令从华为云上下载Hadoop安装文件 # wget -P /opt https://m…

数组的理论知识

文章目录 数组的理论知识 数组的理论知识 数组是我们在编程时期经常使用到的一种数据结构。 特点: 在连续的内存空间中存储相同数据类型的数据 如图: arr 数组 注意点:数组的修改的效率是比较慢的,O(n),因为数组只…

6、phpjm混淆解密和php反序列化

题目:青少年雏形系统 1、打开链接也是一个登入面板 2、尝试了sqlmap没头绪 3、尝试御剑,发现一个www.zip 4、下载打开,有一个php文件打开有一段phpjm混淆加密 5、使用手工解混淆 具体解法链接:奇安信攻防社区-phpjm混淆解密浅谈…

国产化服务器开启NTP功能并向NTP时钟服务器同步

1.备份/etc/chrony.conf文件; cp -rp /etc/chrony.conf /etc/chrony.conf.bak.20240522 2.修改chrony.conf文件,增加NTP时钟信息。(客户端填写时钟同步服务器的IP地址或者域名,我这里写的IP地址。下面Allow NTP Client是只允许…

基于YOLOv8+PySide6的快递分类管理系统

1、背景 随着电子商务的飞速发展,快递行业所承受的数据处理需求愈发庞大。在这样的背景下,传统的手工分类方法已经显得力不从心,因其不仅耗时耗力,还存在着易出错的隐患。因此,迫切需要研发出一套高效而准确的自动化系…

Linux VIM指令

三种模式 命令模式:控制屏幕光标的移动,字符、字或行的删除等输入对文件的一些指令 插入模式:对文件内容进行文字输入 底行摸索:文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作…

GAW-1000D 微机控制钢绞线拉力试验机

一、整机外观图与示意图 外观示意图 性能说明: GAW-1000D型微机控制电液伺服钢绞线拉力试验机主要用于对预应力钢绞线进行抗拉强度测试。由宽调速范围的电液比例伺服阀与计算机及测控单元所组成伺服控制系统,能精确的控制和测量试验全过程。整机由主机…

浮点型比较大小

浮点数的存储形式 浮点数按照在内存中所占字节数和数值范围,可以分为浮点型,双精度浮点型和长双浮点型数。 代码: printf("lgn:%e \n", pow(exp(1), 100));printf("lgn:%f ", pow(exp(1), 100));输出结果: …

Mac上如何安装低版本chrome浏览器

背景 为了排查项目上使用chrome低版本的兼容性问题,需要在本机【mac系统】上安装一个低版本的chrome浏览器。 不同版本的chrome下载地址 https://www.slimjet.com/chrome/google-chrome-old-version.php 下载后要记得你下载的旧版本的版本号,后面修改…

15 VUE学习:插槽slot

插槽内容与出口 这个也是插槽 组件能够接收任意类型的 JavaScript 值作为 props,但组件要如何接收模板内容呢?在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。 举例来说,这…

MySQL——优化

全文搜索最慢 EXPLAIN select * from city; 范围搜索 EXPLAIN select * from city where ID>5 and ID<20; 主键查询 EXPLAIN select * from citywhere ID5; 索引查询 EXPLAIN select * from citywhere CountryCodeNLD; 普通查询 EXPLAIN select * from city where Nam…

想转行程序员的朋友,有什么想问的在评论区随便问,我知道的都告诉你。

你想转行程序员吗&#xff1f; 我自己是法学院毕业后&#xff0c;通过2年的努力才转行程序员成功的。 我发现对于一个外行来说&#xff0c;找不到一个适合自己的方向&#xff0c;光靠努力在一个新的行业里成功异常艰难。即使你非常努力&#xff0c;但方向错了也会做大量的无用…

Mac彩球一直转圈怎么解决 苹果电脑经常卡住 mac电脑运行慢怎么办

用Mac电脑办公的朋友在操作体积比较大的程序时&#xff0c;比如Final Cut Pro或者Photo Shop&#xff0c;一定遇到过鼠标变更彩球并不停转圈。这是怎么回事&#xff1f;今天本文就告诉大家Mac彩球一直转圈怎么解决&#xff0c;以及苹果电脑经常卡住是什么原因。 一、Mac彩球一直…

AI教母李飞飞:现在的AI根本没有主观感觉能力

通用人工智能 (AGI) 是用来描述至少在人类展示&#xff08;或可以展示&#xff09;智能的所有方面都与人类一样聪明的人工智能代理的术语。这就是我们过去所说的人工智能&#xff0c;直到我们开始创建无可否认“智能”的程序和设备&#xff0c;但这些程序和设备只在有限的领域—…

【C++】---多态

【C】---多态 一、多态的概念二、多态的定义及实现1、构成多态的2个必要条件2、什么叫做虚函数的重写&#xff1f;3、虚函数重写的3个例外4、建议把 析构函数 都定义为&#xff1a;虚函数 三、C11的两个关键字&#xff1a;final override1、final&#xff1a;修饰虚函数&#x…

整理了10个靠谱且热门的赚钱软件,适合普通人长期做的赚钱副业

作为一名普通的上班族&#xff0c;我们每天都在辛勤工作&#xff0c;但工资的增长速度却如同蜗牛般缓慢。不过&#xff0c;别担心&#xff0c;信息时代总是带给我们无尽的惊喜&#xff01;今天&#xff0c;我将为大家推荐一些赚钱的宝藏软件&#xff0c;让你在闲暇之余轻松实现…

IPv4 报头 Protocol 字段和 IPv6 报头 Next header 字段中的 IP 协议号列表

IPv4 基本报头&#xff08;20 ~ 60 Byte&#xff09; IPv6 基本报头&#xff08;40 Byte&#xff09; IPv4 Header vs IPv6 Header 黄色 为 IPv6 与 IPv4 相同 红色 为 IPv6 删除的 蓝色 为名称不同功能相同 中青色 为新增的 Type of service Traffic Class &#xff08;用于…

Android ListView鼠标模式下ListView回滚问题

概述 在 Android 应用程序中&#xff0c;ListView 是一种常用的控件&#xff0c;用于显示可滚动列表数据。然而&#xff0c;当在鼠标操作模式下使用 ListView 时&#xff0c;可能会遇到一个问题&#xff1a;点击列表项时&#xff0c;列表会回滚到指定位置&#xff0c;这可能会导…