Rust编程中的线程间通信

1.消息传递

为了实现消息传递并发,Rust 标准库提供了一个 信道channel)实现。信道是一个通用编程概念,表示数据从一个线程发送到另一个线程。

可以将编程中的信道想象为一个水流的渠道,比如河流或小溪。如果你将诸如橡皮鸭或小船之类的东西放入其中,它们会顺流而下到达下游。编程中的信息渠道(信道)有两部分组成,一个发送者(transmitter)和一个接收者(receiver)。发送者位于上游位置,在这里可以将橡皮鸭放入河中,接收者则位于下游,橡皮鸭最终会漂流至此。代码中的一部分调用发送者的方法以及希望发送的数据,另一部分则检查接收端收到的消息。当发送者或接收者任一被丢弃时可以认为信道被 关闭closed)了。

下面的代码我们创建了一个信道但没有做任何事。注意这还不能编译,因为 Rust 不知道想要在信道中发送什么类型:

use std::sync::mpsc;
​
fn main() {
    let (tx, rx) = mpsc::channel();
}

这里使用 mpsc::channel 函数创建一个新的信道;mpsc多个生产者,单个消费者multiple producer, single consumer)的缩写。简而言之,Rust 标准库实现信道的方式意味着一个信道可以有多个产生值的 发送sending)端,但只能有一个消费这些值的 接收receiving)端。想象一下多条小河小溪最终汇聚成大河:所有通过这些小河发出的东西最后都会来到下游的大河。目前我们以单个生产者开始,但是当示例可以工作后会增加多个生产者。

mpsc::channel 函数返回一个元组:第一个元素是发送端 -- 发送者,而第二个元素是接收端 -- 接收者。由于历史原因,txrx 通常作为 发送者transmitter)和 接收者receiver)的缩写,所以这就是我们将用来绑定这两端变量的名字。这里使用了一个 let 语句和模式来解构了此元组;

现在将发送端移动到一个新建线程中并发送一个字符串,这样新建线程就可以和主线程通讯了,代码如下:

use std::sync::mpsc;
use std::thread;
​
fn main() {
    let (tx, rx) = mpsc::channel();
​
    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });
}

这里再次使用 thread::spawn 来创建一个新线程并使用 movetx 移动到闭包中这样新建线程就拥有 tx 了。新建线程需要拥有信道的发送端以便能向信道发送消息。信道的发送端有一个 send 方法用来获取需要放入信道的值。send 方法返回一个 Result<T, E> 类型,所以如果接收端已经被丢弃了,将没有发送值的目标,所以发送操作会返回错误。在这个例子中,出错的时候调用 unwrap 产生 panic。

我们在主线程中从信道的接收者获取值。这类似于在河的下游捞起橡皮鸭或接收聊天信息,代码如下:

use std::sync::mpsc;
use std::thread;
​
fn main() {
    let (tx, rx) = mpsc::channel();
​
    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });
​
    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

信道的接收者有两个有用的方法:recvtry_recv。这里,我们使用了 recv,它是 receive 的缩写。这个方法会阻塞主线程执行直到从信道中接收一个值。一旦发送了一个值,recv 会在一个 Result<T, E> 中返回它。当信道发送端关闭,recv 会返回一个错误表明不会再有新的值到来了。

try_recv 不会阻塞,相反它立刻返回一个 Result<T, E>Ok 值包含可用的信息,而 Err 值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用 try_recv 很有用:可以编写一个循环来频繁调用 try_recv,在有可用消息时进行处理,其余时候则处理一会其他工作直到再次检查。

出于简单的考虑,这个例子使用了 recv;主线程中除了等待消息之外没有任何其他工作,所以阻塞主线程是合适的。

我们将会看到主线程打印出这个值:

2.信道与所有权转移

所有权规则在消息传递中扮演了重要角色,其有助于我们编写安全的并发代码。防止并发编程中的错误是在 Rust 程序中考虑所有权的一大优势。现在让我们做一个试验来看看信道与所有权如何一同协作以避免产生问题:我们将尝试在新建线程中的信道中发送完 val之后 再使用它。

看下面的代码:

use std::sync::mpsc;
use std::thread;
​
fn main() {
    let (tx, rx) = mpsc::channel();
​
    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
        println!("val is {}", val);
    });
​
    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

这里尝试在通过 tx.send 发送 val 到信道中之后将其打印出来。但这么做有个隐患:一旦将值发送到另一个线程后,那个线程可能会在我们再次使用它之前就将其修改或者丢弃。其他线程对值可能的修改会由于不一致或不存在的数据而导致错误或意外的结果。

当编译这段代码时,Rust给出一个错误:

上面的隐患会造成一个编译时错误。send 函数获取其参数的所有权并移动这个值归接收者所有。这可以防止在发送后再次意外地使用这个值;所有权系统检查一切是否合乎规则。

3.发送多个值并观察接收者的等待

先看一段代码:

use std::sync::mpsc;
use std::thread;
use std::time::Duration;
​
fn main() {
    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));
        }
    });
​
    for received in rx {
        println!("Got: {}", received);
    }
}

这一次,在新建线程中有一个字符串 vector 希望发送到主线程。我们遍历它们,单独的发送每一个字符串并通过一个 Duration 值调用 thread::sleep 函数来暂停一秒。

在主线程中,不再显式调用 recv 函数:而是将 rx 当作一个迭代器。对于每一个接收到的值,我们将其打印出来。当信道被关闭时,迭代器也将结束。编译这段代码,结果如下:

因为主线程中的 for 循环里并没有任何暂停或等待的代码,所以可以说主线程是在等待从新建线程中接收值。

4.通过克隆发送者创建多个生产者

之前我们提到了mpscmultiple producer, single consumer 的缩写。可以运用 mpsc 来扩展创建向同一接收者发送值的多个线程。这可以通过克隆发送者来做到, 看下面的代码:

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);
    }

这一次,在创建新线程之前,对发送者调用了 clone 方法。这会给我们一个可以传递给第一个新建线程的发送端句柄。我们会将原始的信道发送端传递给第二个新建线程。这样就会有两个线程,每个线程将向信道的接收端发送不同的消息。编译代码执行结果如下:

不同的系统可能会看到这些值以不同的顺序出现;这也就是并发既有趣又困难的原因。如果通过 thread::sleep 做实验,在不同的线程中提供不同的值,就会发现它们的运行更加不确定,且每次都会产生不同的输出。

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

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

相关文章

Qt执行带参sql

//准备执行的sql语句&#xff0c;此为带参的sql语句query.prepare("update employee set Name:Name, Gender:Gender,Height:Height,"" Birthday:Birthday, Mobile:Mobile, Province:Province,"" City:City, Department:Department, Education:Educati…

农场养殖管理系统软件开发方案

一、项目概述 农场养殖管理系统是一款针对农场养殖管理的软件&#xff0c;旨在提高农场养殖效率和管理水平。本方案将详细介绍该系统的开发流程&#xff0c;包括需求分析、系统设计、数据库设计、界面设计、系统测试和上线运营等方面。 二、需求分析 在开发农场养殖管理系统…

Socket网络编程

本文主要讲解Socket网络编程。 首先介绍socket&#xff0c;包括TCP和UDP通信过程&#xff1b;然后介绍常用的函数&#xff1b;最后编写client-server例子&#xff0c;并进行测试。 文章目录 Socket介绍TCP通信过程服务器端通信过程&#xff1a;客户端通信过程&#xff1a; UDP通…

数据结构线性表——栈

前言&#xff1a;哈喽小伙伴们&#xff0c;今天我们将一起进入数据结构线性表的第四篇章——栈的讲解&#xff0c;栈还是比较简单的哦&#xff0c;跟紧博主的思路&#xff0c;不要掉队哦。 目录 一.什么是栈 二.如何实现栈 三.栈的实现 栈的初始化 四.栈的操作 1.数据入栈…

基于JavaWeb+SSM+校园零售商城微信小程序系统的设计和实现

基于JavaWebSSM校园零售商城微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应…

AYIT-ACM实验室发展历程

AYIT-ACM简介 ACM协会为你的梦想插上翅膀。 本院ACM协会成立于2012年 2008年开始小规模参加河南省竞赛 2014年成功实现金牌零突破 指导老师&#xff1a;孙高飞老师 安阳工学院计算机科学与信息工程学院ACM队是一支优秀的队伍&#xff0c;一支充满活力与激情的队伍&am…

【51单片机】之入门详解(一)

&#x1f4c3;博客主页&#xff1a; 小镇敲码人 &#x1f49e;热门专栏&#xff1a;C语言进阶 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f30f; 任尔江湖满血骨&#xff0c;我自踏雪寻梅香。 万千浮云遮…

MFC 简单绘图与文本编辑

目录 一.创建单文档项目 二.消息映射机制 三.WM_PAINT消息触发 四.CVIEW类 五.设备上下文 六.资源类和资源的关系 七.画线&#xff0c;矩形 八.画布 九.画笔 十.画刷 十一.利用TRACE打印日志 十二.文本编程 十三.ID号 十四.菜单栏 十五.菜单命令路由 十六.工具…

spring boot中使用Bean Validation做优雅的参数校验

一、Bean Validation简介 Bean Validation是Java定义的一套基于注解的数据校验规范&#xff0c;目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本&#xff0c;再到JSR 380的2.0版本&#xff08;2.0完成于2017.08&#xff09;&#xff0c;目前最新稳定版2.0.2&#xff08;201…

Vue3 数据响应式原理:Proxy和Reflect

我们在Vue2中使用的是Object.defineProperty方法来实现数据响应式的&#xff0c;可以通过get和set方法来监听对象的访问和修改。 但是并不能响应对象中属性的增加和删除&#xff0c;只能使用Vue.$set 和Vue.$delete 来对对象中的属性进行增加和删除。 数组也不能直接通过下标…

从单服务设计看SLA保证

文章首发公众号&#xff1a;海天二路搬砖工 0. 引言 在微服务架构中&#xff0c;谈到SLA保证&#xff0c;我们更多是从宏观的角度来需求解决方案。比如&#xff0c;通过合理服务拆分来增加系统整体的可维护性&#xff1b;通过多实例部署来保证系统的灾备。但是单个服务是可靠…

vivado产生报告阅读分析-常规报告1

“ Report Utilization ” &#xff08; 使用率报告 &#xff09; 报告有助于从层级、用户定义的 Pblock 或 SLR 层面来分析含不同资源的设计的使用率。在流程中各步骤间使用 report_utilization Tcl 命令生成“ Utilization Report ”。 以下显示的报告详细信息适用于 Ultr…

Oracle(2-2)Oracle Net Architecture

文章目录 一、基础知识1、Oracle Net Connections Oracle网络连接2、C/S Application Connection C/S应用程序连接3、OSI Communication Layers OSI通信层4、Oracle Protocol Support Oracle协议支持5、B/S Application Connections B/S应用程序连接6、TwoTypes JDBC Drivers 两…

半导体电导率受哪些因素影响?如何正确测量半导体电导率?

半导体的电导率直接影响着半导体器件的工作状态&#xff0c;是半导体材料的重要参数。因此&#xff0c;半导体电导率的检测也是半导体设计和制造过程中的关键环节&#xff0c;确保半导体器件的性能、稳定性和可靠性。 什么是半导体电导率? 半导体电导率是指导电流在单位时间和…

保姆级Decimal.js的使用(如何解决js精度问题)

精度问题控制台图样 如果银行的业务你这样做&#xff0c;不知道要损失多少钱&#xff0c;这样是不行的&#xff0c;计算的不准确是需要背锅的&#xff0c;我们给后端去做吧&#xff0c;其实我们前端也是可以做的&#xff0c;引入Decimal.js 01.引入Decimal.js decimal.js是使用…

2023.11.14-hive之表操作练习和文件导入练习

目录 需求1.数据库基本操作 需求2. 默认分隔符案例 需求1.数据库基本操作 -- 1.创建数据库test_sql,cs1,cs2,cs3 create database test_sql; create database cs1; create database cs2; create database cs3; -- 2.1删除数据库cs2 drop database cs2; -- 2.2在cs3库中创建…

2023NewStarCTF

目录 一、阳光开朗大男孩 二、大怨种 三、2-分析 四、键盘侠 五、滴滴滴 六、Include? 七、medium_sql 八、POP Gadget 九、OtenkiGirl 一、阳光开朗大男孩 1.题目给出了secret.txt和flag.txt两个文件&#xff0c;secret.txt内容如下&#xff1a; 法治自由公正爱国…

MT8788核心板主要参数介绍_联发科MTK安卓核心板智能模块

MT8788核心板是一款功能强大的4G全网通安卓智能模块&#xff0c;具有超高性能和低功耗特点。该模块采用联发科AIOT芯片平台。 MT8788核心板搭载了12nm制程的四个Cortex-A73和四个Cortex-A53处理器&#xff0c;最高主频可达2.0GHZ。它还配备了4GB64GB(2GB16GB、3GB32GB)的内存&a…

新生儿母乳过敏:原因、科普和注意事项

引言&#xff1a; 母乳过敏是一种较为罕见但可能令家长担忧的现象。母亲通常认为母乳是新生儿最安全、最适合的食物&#xff0c;然而有时候宝宝可能对母乳中的某些成分产生过敏反应。本文将科普新生儿母乳过敏的原因&#xff0c;提供相关信息&#xff0c;并为父母和监护人提供…

JTS: 20 InteriorPoint 内部中心点

文章目录 版本代码 版本 org.locationtech.jts:jts-core:1.19.0 链接: github 代码 package pers.stu.algorithm;import org.locationtech.jts.algorithm.InteriorPoint; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; i…