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

文章目录

  • 前言
  • 一、创建线程
  • 二、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.mzph.cn/news/760989.shtml

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

相关文章

未来已来?国内10家AI大模型盘点(附体验网址)

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 1、阿里云——通义千问2、科大讯飞——星火大模…

Unity类银河恶魔城学习记录11-3 p105 Inventory UI源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI_itemSlot.cs using System.Collections; using System.Collections.Gen…

从初学者到专家:Java的Lambda表达式完整指南

一.Lambda的概念 概念:Lambda表达式是Java 8引入的一项重要功能,它允许我们以更简洁和灵活的方式编写代码。可以把Lambda表达式看作是一种更方便的匿名函数,可以像数据一样传递和使用。 使用Lambda表达式可以让我们写出更短、更易读的代码。…

C++ —— 内存管理

目录 1. C内存分布 2. C 内存管理方式 2.1 new 和 delete 操作内置类型 2.2 new 和 delete 操作自定义类型 3. operator new与operator delete函数 4. new和delete的实现原理 5. malloc/free 和 new/delete 的区别 1. C内存分布 首先看一段代码: int globalV…

用Python的turtle库绘制皮卡丘

turtle库的简介 turtle(海龟)库是turtle绘图体系的python实现,turtle库是一种标准库,是python自带的。 turtle(海龟)是一种真实的存在,有一个海龟在窗口的正中心,在画布上游走,走过的轨迹形成了绘制的图形&#xff0…

进阶二叉树

目录 二叉树 二叉搜索树 二叉搜索树的定义 二叉搜索树的操作 哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 哈夫曼树的性质 平衡二叉树 平衡二叉树的定义: 平衡二叉树的插入调整 1.LL插入/LL旋转 2.RR插入/RR旋转 3.LR插入/LR旋转 4.RL插入/RL旋转 二叉树…

仿牛客项目Day10——统一异常处理、记录日志

统一异常处理 在controller里创建advice包,创建ExceptionAdvice类 这个注解括号里面是指只扫描被Controller标注的bean 请求request、响应response、异常exception 普通请求和异步请求的区别在于传的是json还是html吗? 统一记录日志 面向切面编程&…

IO多路复用、域套接字

思维导图 面试题TCP通信中的三次握手和四次: 客户端像向服务器端发送连接请求 服务器应答连接请求 客户端与服务器简历连接 客户端向服务器发送断开请求 服务器应答断开请求 服务器请求关闭连接 客户端发送确认应答 并行和并发的区别: 并行&#xff1a…

外包干了6天,技术明显进步。。。

我是一名大专生,自19年通过校招进入湖南某软件公司以来,便扎根于功能测试岗位,一晃便是近四年的光阴。今年8月,我如梦初醒,意识到长时间待在舒适的环境中,已让我变得不思进取,技术停滞不前。更令…

R语言Meta分析核心技术:从入门到精通

R语言作为一种强大的统计分析和绘图语言,在科研领域发挥着日益重要的作用。其中,Meta分析作为一种整合多个独立研究结果的统计方法,在R语言中得到了广泛的应用。通过R语言进行Meta分析,研究者能够更为准确、全面地评估某一研究问题…

Java毕业设计-基于springboot开发的Java时间管理系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示(源代码在文末)二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、管理员功能模块2、用户功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的Java时间管理系统-毕业论文答…

‘sc‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

问题描述: sc 不是内部或外部命令,也不是可运行的程序 或批处理文件。 通过mysql黑窗口命令 “sc delete 服务名” 删除mysql服务时系统报错:sc不是内部命令或外部命令 原因: 系统默认环境变量值发生变化; 解决&…

从零开始学习typescript系列6: typescript各种类型以及类型特殊使用

基础类型的分类 常用 boolean: 布尔值number: 支持2/8/10/16进制string: 字符串enum: 枚举类型&#xff0c;可根据value找到keyarray: 普通数组&#xff0c;有2种方式&#xff0c;string[]或者 Array<string>tuple: 特殊数组&#xff0c;指定数组里的每个元素的类型&am…

UE snap02 解析ASCII文本文件

UE snap02 解析ASCII文本文件 示例数据data.dat 11389477.2714892 3364559.73645693 0 11389471.5162524 3364567.8860295 0 11389471.5162524 3365813.09618369 0 11388329.6082659 3366184.85895869 0 11388320.4775297 3366197.78833087 0 11388270.6882384 3366214.84811…

爬虫实战-Python爬取百度当天热搜内容

爬虫实战-Python爬取百度当天热搜内容 学习建议学习目标预期内容目标分解热搜地址热搜标题热搜简介热搜指数小总结 代码实现总结 学习建议 本文仅用于学习使用&#xff0c;不做他用&#xff1b;本文仅获取页面的内容&#xff0c;作为学习和对Python知识的了解&#xff0c;不会…

2040X系列 电子校准件

苏/州/新/利/通 2040X系列 电子校准件 300kHz&#xff5e;67GHz 简述 2040X系列电子校准件包括20402/20403/20404/20405/20409五种型号&#xff0c;覆盖频段300kHz&#xff5e;18GHz/10MHz&#xff5e;26.5GHz/10MHz&#xff5e;50GHz/10MHz&#xff5e;20GHz/10MHz&#xf…

redis学习-Set集合类型相关命令及特殊情况分析

目录 1. sadd key value1 value2 ... 2. smembers key 3. sismember key value 4. scard key 5. srem key value1 value2 ... 6. srandmember key num 7. spop key num 8. smove key1 key2 value 9. sdiff key1 key2 key3 ... 10. sinter key1 key2 ... 11. sunion key1 key2 .…

鸿蒙一次开发,多端部署(二)从一个例子开始

本章通过一个天气应用&#xff0c;介绍一多应用的整体开发过程&#xff0c;包括UX设计、工程管理及调试、页面开发等。 UX设计 本示例中的天气应用包含主页、管理城市和添加城市三个页面&#xff0c;其中主页中又包含菜单和更新间隔两个弹窗&#xff0c;基本业务逻辑如下所示…

Aztec的客户端证明

1. 引言 隐私保护 zk-rollup 的证明生成与通用 zk-rollup 的证明生成有很大不同。原因是给定交易中存在特定数据&#xff08;由私有函数处理&#xff09;&#xff0c;我们希望保持完全私有。在本文中&#xff0c;我们探讨了用于证明私有函数正确执行的客户端证明生成&#xff…

arm-linux实现onvif server+WS-UsernameToken令牌验证

目录 一、环境搭建 1、安装openssl 2、安装bison 3、安装flex 二、gsoap下载 三、编译x86版本gsoap 四、编译arm-linux版本gsoap 1、交叉编译openssl 1.1、下载openssl 1.2、交叉编译 2、交叉编译zlib 2.1、下载zlib 2.2、交叉编译 3、交叉编译gsoap 3.1、编译过…