首页 > 其他分享 >rust学习十六.3、并发-线程之间共享数据

rust学习十六.3、并发-线程之间共享数据

时间:2025-01-12 12:21:17浏览次数:1  
标签:线程 unwrap lock 并发 Arc Mutex let rust

线程之间共享数据,即go中部分人所憎恶的方式!

然而,这个方式并非没有其优点,否则操作系统也不提供这种实现方式。

 

闲言少序,上正文!

一、概述

 * 1.当我们大谈基于信道通信时,应该指的是应用级别。如果是操作系统,应该还是会有多种措施的,否则rust的源头在哪里来着。  * 2.rust共享内存,主要借助于Arc指针实现。所谓Arc,是Atomically Reference Counted的缩写,即原子引用计数。 它和Rc的主要  *   区别在于,Arc允许多线程同时访问,而Rc不允许。  * 3.此外还需要借助于Mutex(mutual exclusion 互相排斥之意),即互斥锁,来保证同一时刻只有一个线程可以访问共享资源。  * 4.Mutex的主要方法有lock(),lock用于获取锁,如果成功返回一个MutexGuard智能指针。获取锁的时候,会阻塞线程  * 5.MutexGuard智能指针实现了Deref和Drop,因此可以被当作原始数据的引用,同时Drop确保了当MutexGuard离开作用域时,锁会被释放。  * 6.Mutex 是一个类似RefCell的智能指针,都提供了内部可变性  * 7.Mutex<T> 也有造成 死锁(deadlock)的风险:当某个操作需要锁住两个资源,而两个线程分别持有两个资源的其中一个锁时,它们会永远相互等待  * 8.对于简单的数值运算,标准库中 std::sync::atomic 模块 提供了比 Mutex<T> 更简单的类型。针对基本类型,这些类型提供了安全、并发、原子的操作  *   例如 AtomicI32   Mutex的结构代码
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")]
pub struct Mutex<T: ?Sized> {
    inner: sys::Mutex,
    poison: poison::Flag,
    data: UnsafeCell<T>,
}

 

这个sys::Mutex是一个原子:

pub struct Mutex {
    futex: Atomic,
}
 用于持有锁position(位置标记/占有标记)是一个原子布尔,用于表示是否被占有data就是原始的数据了。  Mutex最常用的方法lock用于获得锁,并返回如下:
 #[stable(feature = "rust1", since = "1.0.0")]
    pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
        unsafe {
            self.inner.lock();
            MutexGuard::new(self)
        }
    }
 这段代码的大概意思:调用操作系统的内部锁,返回一个新的MutexGuard。完整的结果就是包含一个锁和一个结果 

二、示例-来自于书本的

use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let c = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = c.lock().unwrap();
            *num += 1;
            println!("线程{:?}正在执行,得到{}", thread::current().id(),*num);
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}

借用Arc指针(多线程使用的,可以共享数据)和Mutex锁实现简单的数字自增游戏! 很像餐桌上大伙共享一个公勺,有了公勺你要干什么都可以,但一个时刻只有一把公勺!

 

三、示例-特意构建的死锁

根据书本的要求,我特意构建一个死锁的代码。

发生死锁的最主要原因是互相等待对方持有的资源,好比劫匪持有人质,警察要人质,常常会陷入死循环!直到一方崩溃

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
    let 人质 = Arc::new(Mutex::new(5));    
    let 自由 = Arc::new(Mutex::new(10));

    let h0=Arc::clone(&人质);
    let w0=Arc::clone(&自由);
    let 劫匪=thread::spawn(move || {      
        //线程0先获得人质资源,然后等待一段时间再获得自由资源。
        let mut _heigth = h0.lock().unwrap();        
        thread::sleep(Duration::from_millis(10));
        println!("我已经持有人质,快点让路!");
        let mut _自由 = w0.lock().unwrap();
        let area = (*_heigth) * (*_自由);
        println!("线程{:?}正在执行,得到{}", thread::current().id(), area);
    });

    let h1=Arc::clone(&人质);
    let w1=Arc::clone(&自由);
    let 警察=thread::spawn(move || {
        //线程1先获得的自由资源,然后等待一段时间再获得人质资源。
        let mut _自由 = w1.lock().unwrap();
        thread::sleep(Duration::from_millis(10));
        println!("赶紧放了人质,否则爆你狗头!");
        let mut _heigth = h1.lock().unwrap();        
        let area = (*_heigth) * (*_自由);
        println!("线程{:?}正在执行,得到{}", thread::current().id(), area);
    });

    劫匪.join().unwrap();
    警察.join().unwrap();
    
    println!("众人在围观....");
    let  _heigth = 人质.lock().unwrap();
    let  _自由 = 自由.lock().unwrap();
    let area = (*_heigth) * (*_自由);
    println!("Result: {}", area);
}

 

四、小结

  1. rust使用Arc替代Rc指针,以便在多线程下的共有安全
  2. rust使用Mutex互斥锁,保证独享资源
  3. 正确编写代码,避免竞争死锁
  4. 避免死锁,必须遵循一些原则,例如
    • 尽量减少一个线程同时持有多个锁的情况。如果一个线程确实需要持有多个锁,应该确保所有锁都按照相同的顺序被获取和释放
    • 使用尝试锁(TryLock)
    • 设置锁超时
    • 检测和恢复死锁

       避免死锁的方法,有多种,这里只能列出其中一二比较简单有效的实践。

 

标签:线程,unwrap,lock,并发,Arc,Mutex,let,rust
From: https://www.cnblogs.com/lzfhope/p/18664452

相关文章

  • 【Rust】枚举与模式匹配
    目录思维导图一、概述1.枚举的定义与使用2.特殊枚举:Option4.模式匹配5.iflet构造二、枚举1.枚举的定义与作用2.IP地址的枚举示例示例代码3.结构体与枚举的组合示例代码4.枚举变体的灵活性示例代码5.枚举的方法代码示例:6.Option枚举的优势标准库......
  • workerpool,JavaScript强大的线程池库!
    ......
  • MySQL sleep 线程过多怎么解决
    1知道sleep线程过多原因首先要知道到底是什么原因导致的sleep线程过多的:程序逻辑问题,导致连接一直不释放;mysql参数的问题,是不是参数配置的不合理,一直不释放连接;mysql语句的问题,数据库查询不够优化,过度耗时。大并发情况问题,导致sleep情况过多;2临时解决s......
  • 1.4. 线程状态转化及源码解读
    2.init()packageThreadPackage;publicclassThreadTest{publicstaticvoidmain(String[]args){//创建一个线程,显式调用Thread的构造函数Threadthread=newThread(()->{System.out.println("线程运行");},"M......
  • 整理字节腾讯阿里等数百份大厂面经:Java多线程和线程安全最高频面试题及参考答案
    多线程(并发编程)和线程安全几乎是每场面试必问的问题,下面面试题是从字节跳动、腾讯和阿里等几百份的面试题整理的,面试时出现频率很高的。目录Java对锁的优化机制是怎样的?无锁是怎么回事?CAS锁原理是什么?它跟CPU底层的指令有关系吗?ABA问题是怎么回事?说说synchronized和......
  • Lec 10 线程
    Lec10线程License本内容版权归上海交通大学并行与分布式系统研究所所有使用者可以将全部或部分本内容免费用于非商业用途使用者在使用全部或部分本内容时请注明来源资料来自上海交通大学并行与分布式系统研究所+材料名字对于不遵守此声明或者其他违法使用本内容者,将依法保......
  • JAVA并发编程系列 (二)
             目录1.javastart如何调用到run方法?2.synchronized关键字的底层原理,synchronize锁是如何实现的?3.notify和notifyAll区别4.synchronize锁优化锁膨胀过程?5.AQS原理6.ReentrantLock和synchronized区别7.Lock高级功能?8.简述下CAS?9.int......
  • Dart语言的并发编程
    Dart语言的并发编程引言在现代软件开发中,处理并发操作是一项重要且复杂的任务。无论是为了提高应用程序的响应速度,还是为了有效利用系统资源,掌握并发编程都是开发者必备的技能。在众多编程语言中,Dart语言以其简洁性和高效性吸引了大量开发者的关注,尤其是在Flutter框架的推......
  • Erlang语言的并发编程
    Erlang语言的并发编程引言并发编程是现代计算机科学中一个重要的研究领域。随着多核处理器的普及,如何有效地利用这些处理器成为了开发者面临的重大挑战。Erlang语言因其独特的设计理念而在并发编程领域中脱颖而出,广泛应用于实时系统和分布式应用中。本文将详细探讨Erla......
  • java-多线程(一)
    线程线程是操作体统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。并发和并行并发:在同一时刻,有多个指令在单个CPU上交替执行。并行:在同一时刻,有多个指令在多个cpu上同时执行。多线程的实现方式继承Thread类的方式进行实现。多线程的第一种启......