rust学习十六.2、并发-利用消息传递进行线程间通讯

news/2025/1/9 19:24:29/文章来源:https://www.cnblogs.com/lzfhope/p/18662671

通过信道是rust的解决线程之间通信的2个工具之一,另外1个是是共享内存状态。

rust推出这个,明显地是因为受到go之类的影响。

在书籍中,作者提到go编程文档中的内容:

不要通过共享内存来通讯;而是通过通讯来共享内存(Do not communicate by sharing memory; instead, share memory by communicating)

 

因为,我这里就按部就班复述下书本上关于这个篇章的内容。

 

一、概述

* 1.mpsc 是 multiply producer single consumer 的缩写,即多生产者单消费者模式.意味着
*   rust的主要消息模式不允许一个生产者多个消费者。但rust也没有说不支持一个生产者多个消费者模式
* 2.(tx, rx) = mpsc::channel() 会返回一个元组,分别表示发送和接收者
* 3. rx有两个重要方法,分别是recv() 和 try_recv().前者会阻塞,后者不会。
* 4. 信道和所有权问题. tx.send方法会默认夺取消息的所有权,移动这个值归接收者所有.所以发送者需要拥有消息的所有权。
*    这也同时意味着,如果没有特别措施,那么发送之后,被发送的消息不能再访问了。
*    那么,我们是否宁愿clone()一下,然后再发送呢?是的。
*    rust的所有权系统,导致rust会把消息视为一个实物,而不是一个拷贝,意味着发送就是“脱手”、“出手”
* 5. 发送者可以关闭信道,这样接收者就会收到一个错误。
* 6. 发送者可以克隆,这样多个线程都可以发送消息。发送者虽然克隆了,但是接收者还是同一个.
  7. 这种方式是否可能存在这样的问题:后发先至。 这个问题暂时没有明确答案,因为暂时没有找到答案。据说概率很小,但不是么有?

二、简要示例

2.1例子一

这个例子把原书的简单地做了一些修改,以便更有乐趣些!

和原书稍微不同的是,接收者会根据消息来源把收到的内容分别保存到v0,v1的向量中,最后打印各自拼接的消息

大体模拟了碟战中,收到不同消息来源的情景!

use std::sync::mpsc;
use std::thread;
use std::time::Duration;
#[derive(Debug)]
#[allow(dead_code)]
struct Message {sender: String, // 发送者标识content: String, // 消息内容order: u8, // 消息序号

}fn main() {// --snip--let (tx, rx) = mpsc::channel();   //channel() 返回一个元组,分别表示发送和接收者let tx1 = tx.clone();thread::spawn(move || {           // 移动tx1所有权到新线程中。为什么需要move?规定就是这样let vals = vec![Message{sender: String::from("x1"),content: String::from(""),order:1},Message{sender: String::from("x1"),content: String::from(""),order:2},Message{sender: String::from("x1"),content: String::from(""),order:3},Message{sender: String::from("x1"),content: String::from(""),order:4}];for val in vals {tx1.send(val).unwrap();println!("tx1 is sending....");thread::sleep(Duration::from_millis(200));}});thread::spawn(move || {let vals = vec![Message{sender: String::from("x0"),content: String::from(""),order:1},Message{sender: String::from("x0"),content: String::from(""),order:2},Message{sender: String::from("x0"),content: String::from(""),order:3},Message{sender: String::from("x0"),content: String::from(""),order:4}];for val in vals {tx.send(val).unwrap();println!("tx0 is sending....");thread::sleep(Duration::from_millis(100));}});//把收到的消息分别放入到两个Vec中,以便于观察顺序。let mut v0 = Vec::new();let mut v1 = Vec::new();let mut qty=0;//利用rx的迭代器接收消息. 无需显示调用rec(),或者try_recv(),迭代器会自动调用。for received in rx {if received.sender == "x0" {v0.push(received);}else{v1.push(received);}qty += 1;if qty==5 { // 收到5条消息就退出循环。看看会不会有什么问题break;}}let msg0 = get_msg(v0);let msg1 = get_msg(v1);println!("从t0收到的消息是: {}", msg0);println!("从t1收到的消息是: {}", msg1);if msg0!=msg1 {println!("那一份消息是真的?应该信任谁?还是说都是真的?\n 如果都是真的,为什么会这样?");}}fn get_msg(msg:Vec<Message>) -> String   {let mut result = String::new();for m in msg {result.push_str(&m.content);}return result;
}

当主线程中,设置为接收5条消息:

当主线程中,设置为接收20条(根本不可能):

 

2.2 例子2_try_recv()

很遗憾,第一个例子么有try_recv(),所以这里采用这个试试。

根据描述try_recv()不阻塞主线程,所以可以干一些其它的事情!!!这是我感兴趣的地方,所以把例子稍微修改了下!

//利用try_recv()接收消息,并打印出来。
    loop {match rx.try_recv() {Ok(val) => {if val.sender == "x0" {v0.push(val);}else{v1.push(val);}qty+=1;}Err(_) => {if qty==8 {break;}thread::sleep(Duration::from_millis(200));}}println!("...快点啊!");}

其余部分同示例1,具体略。

结果如下:

用这个loop结构,有个问题:如何知道消息已经接收完毕了? 示例代码只能写死。 但实际应该不能这样,太不友好!

从这个方面来说,不如用迭代友好! 反之,try_recv()让计算机可以干一点别的事情...,充分利用资源,如果有的话!

2.3、例子3 -不接收

use std::sync::mpsc;
use std::thread;
use std::time::Duration;
#[derive(Debug)]
#[allow(dead_code)]
struct Message {sender: String, // 发送者标识content: String, // 消息内容order: u8, // 消息序号

}fn main() {// --snip--let (tx, rx) = mpsc::channel();   //channel() 返回一个元组,分别表示发送和接收者let tx1 = tx.clone();let handle1=thread::spawn(move || {           // 移动tx1所有权到新线程中。为什么需要move?规定就是这样let vals = vec![Message{sender: String::from("x1"),content: String::from(""),order:1},Message{sender: String::from("x1"),content: String::from(""),order:2},Message{sender: String::from("x1"),content: String::from(""),order:3},Message{sender: String::from("x1"),content: String::from(""),order:4}];for val in vals {tx1.send(val).unwrap();println!("tx1 is sending....");thread::sleep(Duration::from_millis(200));}});let handle0=thread::spawn(move || {let vals = vec![Message{sender: String::from("x0"),content: String::from(""),order:1},Message{sender: String::from("x0"),content: String::from(""),order:2},Message{sender: String::from("x0"),content: String::from(""),order:3},Message{sender: String::from("x0"),content: String::from(""),order:4}];for val in vals {tx.send(val).unwrap();println!("tx0 is sending....");thread::sleep(Duration::from_millis(100));}});handle0.join().unwrap();handle1.join().unwrap();
}

 

三、疑问

1.后发先到问题

这个暂时没有结论! 待后续补充!!

2.如何中断发送或者接收

接收者退出即可,即不执行recv,或者try_recv即可!

3.如果没有接收动作,会发送得出去吗?

可以,因为没有报告异常!!!

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

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

相关文章

2025,谁会成为 AI Agent 的新入口?|播客《编码人声》

「编码人声」是由「RTE开发者社区」策划的一档播客节目,关注行业发展变革、开发者职涯发展、技术突破以及创业创新,由开发者来分享开发者眼中的工作与生活。2024 年末,一群来自 Android、Chrome OS、Oculus 等操作系统的开发元老联合创业,推出 AI Agent 操作系统 /dev/agen…

【Windows内核】Ntdll解除挂钩学习

简介 我们之前都是直接通过使用直接系统调用的方式来绕过用户态钩子,通过在项目文件中创建并调用系统调用来实现此目标。还有另外一种方法也可以绕过用户态的钩子,那么这种方法是将已经加载到进程中的钩子DLL替换为一个未经修改且未被钩主的版本来达到相同的目标。 将勾住的D…

21. 主窗口控件

一、主窗口控件之前,我们使用可视控件创建具体的实例时都选择了一个父窗体,将控件放到这个窗体上,即使在创建实例时没有选择父窗体,也可以用控件的 setParent() 方法将控件放到父窗体上。如果一个控件没有放到任何窗体上,则这个控件可以单独成为窗口,并且可以作为父窗口来…

智慧帮手:一站式搭建帮助中心,优化客户服务体验

在数字化时代,客户服务已成为企业赢得市场、留住客户的关键。一个高效、便捷的帮助中心,不仅能够快速响应客户需求,还能提升客户满意度和忠诚度。然而,如何搭建一个既满足客户需求又符合企业特色的帮助中心,成为众多企业面临的挑战。本文将探讨如何借助一站式解决方案,如…

解锁餐饮新篇:精准搭建内部知识库,赋能高效运营

在当今竞争激烈的餐饮行业中,提升运营效率和服务质量已成为企业脱颖而出的关键。随着餐饮业务的不断扩展,内部信息管理和知识传承成为了一大挑战。如何构建一个高效、精准的内部知识库,成为解锁餐饮新篇章的重要一环。本文将探讨如何借助智能化工具,如HelpLook,实现这一目…

【YashanDB知识库】进行load data的时候报找不到动态库liblz4.so

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7863047.html?templateId=1718516 现象 23.2版本的依赖项准备里指明,要依赖动态库:liblz4.so,liblz4.so.1,liblz4.so.1.9.3 在执行load data的时候报找不到动态库liblz4.so 操作系统在/lib64/目…

JAVA-Day 08:For循环语句

For循环 For循环格式 for循环语句格式 for(初始化语句;条件判断语句;条件控制语句){ 循环语句体; } 执行流程: 1、执行初始化语句 2、执行条件判断语句,看其结果是True还是False,如果是True,执行循语句体,如果是False,循环结束 3、执行条件控制语句 4、回到2继续执行条件判…

记录---JS 的蝴蝶效应 —— 事件流

🧑‍💻 写在开头 点赞 + 收藏 === 学会🤣🤣🤣前言 在 JavaScript 的世界里,事件流就像一只永不停歇的蝴蝶,每一个动作、每一个点击、每一个滚动,都会触发一连串的蝴蝶效应。作为一名开发者,掌握事件流的艺术,不仅能让你的网页更加生动、更加交互,也能让用户体验…

HTTPS 加密机制

HTTPS 概述 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。…

leetcode131 分割回文串

leetcode 131思路:回溯 比如说aab,对于每个元素currentNum,有两种选择: 1.如果currentNum<len-1,可以将当前元素加入到currentStr中,然后dfs(start,currentNum+1)。而currentNum==len-1时不能dfs(start,currentNum+1),这样下一轮循环就执行以下代码了 if (currentNum…

Fake Location强制升级解决办法

Fake Location强制升级解决办法 一、下载链接中的软件并安装其中的APK https://www.123865.com/s/TXeiTd-fla7d 二、进入LSPosed软件,点击应用伪装三、勾选Fake Location四、进入应用伪装软件并点击Fake Location五、往下滑,找到应用版本号,并填写为1599, 然后点击右上角的…

【分布式】优雅实现多系统一致性补偿方案

前言 我们在开发的过程中,如果一个业务操作需要本地写MYSQL数据以及对第三方系统做写操作,那么这种流程就涉及到分布式系统一致性的问题,然而并非所有系统都能使用成熟的分布式事务方案 案例说明 以一个财务报账业务为例,涉及到的系统如下:系统名 作用 实现方案单据系统 申…