【一起学Rust | 基础篇】rust线程与并发

news2024/11/15 13:36:11

文章目录

  • 前言
  • 一、创建线程
  • 二、mpsc多生产者单消费者模型
    • 1.创建一个简单的模型
    • 2.分批发送数据
    • 3. 使用clone来产生多个生产者
  • 三、共享状态:互斥锁
    • 1. 创建一个简单的锁
    • 2. 使用互斥锁解决引用问题


前言

并发编程(Concurrent programming),指的是程序的不同部分相互独立的执行。而并行编程(parallel programming)代表程序不同部分于同时执行,这两个概念随着计算机越来越多的利用多处理器的优势时显得愈发重要。由于历史原因,在此类编程中一直是困难且容易出错的:Rust
希望能改变这一点。

在大部分现代操作系统中,已执行程序的代码在一个
进程(process)中运行,操作系统则负责管理多个进程。在程序内部,也可以拥有多个同时运行的独立部分。运行这些独立部分的功能被称为 线程(threads)。


一、创建线程

Rust创建线程是通过thread::spawn函数来创建的,我们只要通过这个函数,并且传入一个闭包即可创建出一个线程。

以两个线程同时输出数字为例,主线程输出1到5,子线程输出1到10。其代码如下所示

thread::spawn函数内的闭包就是子线程的内容,以外的全都是主线程的内容。

thread::sleep是一个线程延时的函数,需要传入DurationDuration是个表示时间单位的,这里Duration::from_millis(1)代表1毫秒,Rust支持更加精确的时间,你可以去看一下这个类,支持微秒纳秒等。

    thread::spawn(|| {
        for i in 1..10 {
            println!("数字是:{},来自spawn创建的线程", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 1..5 {
        println!("数字是:{},来自主线程线程", i);
        thread::sleep(Duration::from_millis(1));
    }

这样就创建好一个线程了,执行效果如下图
从图中可以看出主线程输出了从1到4的数字编号,因为到了5的时候已经执行完毕了,但是子线程却是执行到了5,并没有向后执行,这是因为主线程已经结束了,此时会把子线程也销毁,简而言之就是子线程的生存周期超出了主线程的生存周期,主线程提前结束。

原因:无法保证其执行顺序,主线程提前结束

为了解决这个,就需要接收这个线程的返回值,然后调用join方法来让主线程等待子线程执行完毕再结束运行。

thread::spawn返回一个JoinHandle,其有一个join方法,可以让主线程等待该线程完毕后再结束。

JoinHandle 是一个拥有所有权的值,当对其调用 join 方法时,它会等待其线程结束。join 放在循环之前可以实现先执行子线程【这种操作会变得同步,影响程序运行,因此需要方队地方,仔细斟酌】

处理以上的问题,因此对代码进行一下改动

    let handle =thread::spawn(|| {
        for i in 1..10 {
            println!("数字是:{},来自spawn创建的线程", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    
    for i in 1..5 {
        println!("数字是:{},来自主线程线程", i);
        thread::sleep(Duration::from_millis(1));
    }
    handle.join().unwrap();

此时代码正常运行,结果如下图所示
如果将handle.join().unwrap();放到主线程for循环之前则会出现同步运行的效果,如下图所示

move关键字主要的作用是用来避免在使用闭包的时候遇到的所有权问题的,它起到的一个作用就是在你代码有疑问时,会提示你代码哪里错了,为什么错了。当然,你也可以理解为捕获了外部的变量的所有权。

move关键字的使用是相当简单的,只要在闭包前面加上move就可以了,会自动进行变量的捕获。示例代码如下所示

let v = vec![1, 2, 3];
    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });
    handle.join().unwrap();

二、mpsc多生产者单消费者模型

mpsc多个生产者,单个消费者(multiple producer, single consumer)的缩写。简而言之,Rust 标准库实现信道的方式意味着一个信道可以有多个产生值的 发送(sending)端,但只能有一个消费这些值的 接收(receiving)端。

1.创建一个简单的模型

mpsc是通过mpsc::channel来创建的,返回一个发送者(tx),一个接收者(rx)。

创建一个简单的模型代码如下,发送端在线程内部发送一个字符串Hello,然后接收端接收这个字符串。

let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let hello = String::from("Hello");
        tx.send(hello)
    });
    let received = rx.recv().unwrap();
    println!("接收到的数据是:{}", received)

代码运行效果如下

2.分批发送数据

在使用时,发送端可能会发送多次数据,或者是分批来发送数据,如果再使用

let received = rx.recv().unwrap();

来接收数据,则会起不到想要的效果,因为它只能接收一次,所以可以使用for循环来接收数据

for received in rx {
        println!("接收到的值: {}", received);
    }

这样只要是发送端发送的数据,接收端就都能接收到了,详细代码如下

    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];
        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    // 不再显式调用 recv 函数
    for received in rx {
        println!("接收到的值: {}", received);
    }

3. 使用clone来产生多个生产者

let (tx, rx) = mpsc::channel();
    
    let tx1 = tx.clone();
    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];
    
        for val in vals {
            tx1.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    thread::spawn(move || {
        let vals = vec![
            String::from("more"),
            String::from("messages"),
            String::from("for"),
            String::from("you"),
        ];
    
        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    for received in rx {
        println!("Got: {}", received);
    }

三、共享状态:互斥锁

如果你对多线程开发有所了解,就一定了解过锁的概念。

1. 创建一个简单的锁

let m = Mutex::new(5);
    {
        // 使用 lock 方法获取锁,以访问互斥器中的数据。
        // 如果另一个线程拥有锁,并且那个线程 panic 了,则 lock 调用会失败。
        // MutexGuard也提供了一个 Drop 实现当离开作用域时自动释放锁
        let mut num = m.lock().unwrap();
        *num = 6;
    }
    println!("Mutex:{:?}", m);

2. 使用互斥锁解决引用问题

let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let counter = Arc::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!("Result: {}", *counter.lock().unwrap());

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

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

相关文章

【Java Web基础】一些网页设计基础(三)

文章目录 1. 导航栏样式进一步调整2. 入驻企业信息展示栏2.1 Title设置2.2 具体信息添加 3. 轮播图4. 注册登录按钮及其他信息5. 一些五颜六色的、丰富视觉效果的中间件…… 1. 导航栏样式进一步调整 这种导航栏,选中的时候字体变蓝色,可能还是不够美观&…

C++进阶--哈希

哈希概念 哈希(Hash)是一种常见的密码学技术和数据结构,它将任意长度的输入通过散列算法转换成固定长度的输出,这个输出被称为散列值或哈希值。哈希函数是一种单向函数,即从哈希值无法反推出原始输入值。 哈希函数具有…

Android14 - AMS之Activity启动过程(1)

Android14 - AMS之Activity启动过程(2)-CSDN博客 ​​​​​​​ Android14 - AMS之Activity启动过程(3)-CSDN博客 我们以Context的startActivity场景(option null, FLAG_ACTIVITY_NEW_TASK)来…

C++类型转换及IO流(深度剖析)

文章目录 1. 前言2. C语言的类型转换3. C的强制类型转换3.1 static_cast3.2 reinterpret_cast3.3 const_cast3.4 dynamic_cast 4. RTTI(了解)5. C语言的输入输出及缓存区理解6. CIO流6.1 C标准IO流6.2 C文件IO流 7. stringstream的简单介绍 1. 前言 C语言…

机器学习-可解释性机器学习:支持向量机与fastshap的可视化模型解析

一、引言 支持向量机(Support Vector Machine, SVM)作为一种经典的监督学习方法,在分类和回归问题中表现出色。其优点之一是生成的模型具有较好的泛化能力和可解释性,能够清晰地展示特征对于分类的重要性。 fastshap是一种用于快速计算SHAP值&#xff08…

华曦传媒陆锋:数字媒体时代,社区电梯广告价值正在被重估

在数字化时代的浪潮中,电梯广告、停车场道闸广告、门禁灯箱广告等线下社区广告似乎面临着生存的挑战。 然而,这一传统广告形式展现出了惊人的韧性和价值。 比如,2023年上半年,作为行业龙头分众传媒,2023年上半年实现…

【Linux】多线程编程基础

💻文章目录 📄前言🌺linux线程基础线程的概念线程的优缺点线程与进程的区别 线程的创建 🌻linux线程冲突概念互斥锁函数介绍加锁的缺点 📓总结 📄前言 无论你是否为程序员,相信多线程这个词汇应…

小白也能在3分钟完成短剧解说的剪辑,这是真的!

3分钟的解说视频,真的需要1小时的手工剪辑吗? 生成解说视频需要经过素材准备、解说词创作、声音录制、视频剪辑和视频合成等多个步骤,每个步骤都需要投入一定的时间和精力,因此整个过程较为耗时耗力。 1. 素材准备: 需…

【LINUX笔记】驱动开发框架

应用程序调动驱动程序 驱动模块运行模式 模块加载-卸载 加载卸载注册函数 加载 驱动编译完成以后扩展名为.ko,有两种命令可以加载驱动模块: insmod和modprobe 驱动卸载 驱动注册注销 //查看当前已经被使用掉的设备号 cat /proc/devices 实现设备的具…

AI系统性学习06—开源中文语言大模型

1、ChatGLM ChatGLM-6B的github地址:https://github.com/THUDM/ChatGLM-6B ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级…

【Java Web基础】一些网页设计基础(二)

文章目录 1. Bootstrap导航栏设计1.1 代码copy与删减效果1.2 居中属性与底色设置1.3 占不满问题分析1.4 字体颜色、字体大小、字体间距设置1.5 修改超链接hover颜色,网站首页字体颜色 1. Bootstrap导航栏设计 1.1 代码copy与删减效果 今天设计导航栏,直…

第4关:创建工程项目表J,并插入数据

任务描述 工程项目表J由工程项目代码(JNO)、工程项目名(JNAME)、工程项目所在城市(CITY)组成。创建工程项目表J(JNO,JNAME,CITY),并在J表中插入下图数据。 相关知识 1、MySQL创建表的基本语法如下: 其中,table_name 是要创建的表的名称&…

Hololens 2应用开发系列(4)——MRTK基础知识及配置文件配置(下)

Hololens 2应用开发系列(4)——MRTK基础知识及配置文件配置(下) 一、前言二、边界系统(Boundary)三、传送系统(Teleport)四、空间感知系统(Spatial Awareness&#xff09…

Pytorch神经网络-元组/列表如何喂到神经网络中

📚博客主页:knighthood2001 ✨公众号:认知up吧 (目前正在带领大家一起提升认知,感兴趣可以来围观一下) 🎃知识星球:【认知up吧|成长|副业】介绍 ❤️感谢大家点赞👍&…

设计编程网站集:生活部分:饮食+农业,植物(暂记)

这里写目录标题 植物相关综合教程**大型植物:****高大乔木(Trees):** 具有坚硬的木质茎,通常高度超过6米。例如,橡树、松树、榉树等。松树梧桐 **灌木(Shrubs):** 比乔木…

基于Jenkins + Argo 实现多集群的持续交付

作者:周靖峰,青云科技容器顾问,云原生爱好者,目前专注于 DevOps,云原生领域技术涉及 Kubernetes、KubeSphere、Argo。 前文概述 前面我们已经掌握了如何通过 Jenkins Argo CD 的方式实现单集群的持续交付&#xff0c…

基于Springboot的在线投稿系统+数据库+免费远程调试

项目介绍: Javaee项目,springboot项目。采用M(model)V(view)C(controller)三层体系结构,通过Spring SpringBoot Mybatis VueMavenLayui来实现。MySQL数据库作为系统数据储存平台&a…

Java安全 反序列化(3) CC1链-TransformedMap版

Java安全 反序列化(3) CC1链-TransformedMap版 本文尝试从CC1的挖掘思路出发,理解CC1的实现原理 文章目录 Java安全 反序列化(3) CC1链-TransformedMap版配置jdk版本和源代码配置前记 为什么可以利用一.CC链中的命令执行我们可以尝试一下通过InvokerTransformer.tr…

分布式异步任务框架celery

Celery介绍 github地址:GitHub - celery/celery: Distributed Task Queue (development branch) 文档地址:Celery - Distributed Task Queue — Celery 5.3.6 documentation 1.1 Celery是什么 celery时一个灵活且可靠的处理大量消息的分布式系统&…

数据库关系运算理论:传统的集合运算概念解析

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…