郑州网站建设讯息网站建设遵循的原则
news/
2025/9/23 7:16:10/
文章来源:
郑州网站建设讯息,网站建设遵循的原则,页面设计标准规范,唐山市城乡建设局网站系列文章目录
【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…系列文章目录
【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学 Rust 编程】六、枚举和模式匹配 【跟小嘉学 Rust 编程】七、使用包(Packages)、单元包(Crates)和模块(Module)来管理项目 【跟小嘉学 Rust 编程】八、常见的集合 【跟小嘉学 Rust 编程】九、错误处理(Error Handling) 【跟小嘉学 Rust 编程】十一、编写自动化测试 【跟小嘉学 Rust 编程】十二、构建一个命令行程序 【跟小嘉学 Rust 编程】十三、函数式语言特性迭代器和闭包 【跟小嘉学 Rust 编程】十四、关于 Cargo 和 Crates.io 【跟小嘉学 Rust 编程】十五、智能指针(Smart Point) 【跟小嘉学 Rust 编程】十六、无畏并发(Fearless Concurrency) 文章目录 系列文章目录[TOC](文章目录) 前言一、进程(Process)与线程Thread)1.1、进程(Process)1.2、线程(Thread)1.2.1、线程1.2.2、多线程可能导致的问题1.2.3、实现线程方式 二、多线程开发2.1、使用Thread::spwan 创建线程函数2.2、使用JoinHandle的join 方法等待所有线程执行完毕2.3、使用 move 闭包2.3、使用消息传递2.3.1、使用消息传递2.3.2、通道(Channel)2.3.2.1、使用mpsc::channel 创建 Channel2.3.2.2、发送端 send 方法2.3.2.3、接收端的2.3.2.4、发送多个值和接收等待2.3.2.5、使用 clone 来创建多个发送者 2.4、共享状态并发(Shared-State Concurrency)2.4.1、使用 Mutex 每次只允许一个线程来访问数据2.4.1.1、mutex介绍2.4.1.2、mutex的规则2.4.1.3、MutexT的API2.4.1.4、在多线程中共享 Mutext 与原子引用计数2.4.1.5、RefCell/RC和Mutex/ARC 2.5、通过 Send Trait 和Sync Trait 来扩展并发2.5.1、Send 允许线程间转移所有权2.5.2、Sync 允许线程间转移所有权2.5.3、手动实现 Send 和 Sync是不安全的 总结
前言
Rust无畏并发允许你编写没有细微Bug的代码并在不引入新Bug 的情况下易于重构。 并发包含如下两种
Concurrent程序的不同部分之间独立的执行parallel程序的不同部分同时运行
主要教材参考 《The Rust Programming Language》 一、进程(Process)与线程Thread)
1.1、进程(Process)
在大部分OS里面代码运行在进程中OS同时管理多个进程。
1.2、线程(Thread)
1.2.1、线程
在你程序各自独立部分可以同时运行运行这些独立部分的就是线程(Thread)。
多线程运行的好处
提升性能表现增加复杂性无法保障各线程的执行顺序
1.2.2、多线程可能导致的问题
竞争状态线程以不一致的顺序访问数据或资源死锁两个线程彼此等待对方使用完所持有的自由线程无法继续只有在某些情况下发生的BUG很难可靠地复制现象和修复
1.2.3、实现线程方式
通过调用 OS的 API 来创建线程 1:1 模型需要较小的运行时语言自己实现的线程(绿色线程)M:N模型需要更大的运行时
Rust 需要权衡运行时的支持Rust 标准库仅提供 1:1 模型 的线程
二、多线程开发
2.1、使用Thread::spwan 创建线程函数
use std::thread;
use std::time::Duration;fn main() {thread::spawn(|| {for i in 1..10 {println!(hi number {} from the spawned thread!, i);thread::sleep(Duration::from_millis(1));}});for i in 1..5 {println!(hi number {} from the main thread!, i);thread::sleep(Duration::from_millis(1));}
}主线程执行完其他线程还没执行完
2.2、使用JoinHandle的join 方法等待所有线程执行完毕
thread::spawn 函数返回的是 JoinHandle。JoinHandle 持有值的所有权调用其 join 方法可以等待对应的其他线程的完成。
use std::thread;
use std::time::Duration;fn main() {let handle thread::spawn(|| {for i in 1..10 {println!(hi number {} from the spawned thread!, i);thread::sleep(Duration::from_millis(1));}});for i in 1..5 {println!(hi number {} from the main thread!, i);thread::sleep(Duration::from_millis(1));}handle.join().unwrap();
}2.3、使用 move 闭包
move 闭包通常和 thread::spawn 一起使用它允许你使用其他线程的数据创建线程的时候把值的所有权从一个线程转移到另外一个线程。
范例错误示范
use std::thread;fn main() {let v vec![1, 2, 3];let handle thread::spawn(|| {println!(Heres a vector: {:?}, v);});handle.join().unwrap();
}此时存在有如下错误信息
error[E0373]: closure may outlive the current function, but it borrows v, which is owned by the current function-- src/main.rs:6:32|
6 | let handle thread::spawn(|| {| ^^ may outlive borrowed value v
7 | println!(Heres a vector: {:?}, v);| - v is borrowed here|
note: function requires argument type to outlive static-- src/main.rs:6:18|
6 | let handle thread::spawn(|| {| __________________^
7 | | println!(Heres a vector: {:?}, v);
8 | | });| |______^
help: to force the closure to take ownership of v (and any other referenced variables), use the move keyword|
6 | let handle thread::spawn(move || {|
根据提示信息我们可以使用 move 关键字来修改
use std::thread;fn main() {let v vec![1, 2, 3];let handle thread::spawn(move|| {println!(Heres a vector: {:?}, v);});handle.join().unwrap();
}2.3、使用消息传递
2.3.1、使用消息传递
一种很流行且能够保证安全并发的技术就是消息传递线程或者 Actor 通过彼此发送消息(数据)来进行通信。
Go语言名言不要用共享内存来通信要用通信来共享内存
Rust 使用标准库中的Channel来
2.3.2、通道(Channel)
Channel 包含发送端和接收端调用发送端端方法发送数据接收端会检查和接受到达的数据如果其中一段被丢弃了那么 通道就关闭了。
2.3.2.1、使用mpsc::channel 创建 Channel
mpsc(multiple producer single consumer)多个生产者一个消费者返回一个 tuple里面元素分别是发送端和接收端。
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 recevied rx.recv().unwrap();println!(Got :{}, recevied);
}2.3.2.2、发送端 send 方法
参数要发送的数据返回 ResultT,E,如果有问题就返回一个错误。 会移交所有权
2.3.2.3、接收端的
1、recv 方法
阻止当前线程执行直到 Channel 中有值被送来一旦有值被收到就返回ResultT,E当发送端关闭就会收到一个错误
2、try_recv 方法
不会阻塞立即返回 ResultT,E有数据达到返回OK里面包含数据否则返回错误。
通常使用循环来检查 try_recv 的结果。
2.3.2.4、发送多个值和接收等待
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);}
}2.3.2.5、使用 clone 来创建多个发送者
use std::sync::mpsc;
use std::thread;
use std::time::Duration;fn main() {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);}
}2.4、共享状态并发(Shared-State Concurrency)
Rust 支持通过共享状态实现并发Channel类似单所有权一旦值的所有权转移给Channel 就无法使用了。而共享内存并发类似多所有权多个线程可以同时访问同一块内存。
2.4.1、使用 Mutex 每次只允许一个线程来访问数据
2.4.1.1、mutex介绍
Mutex 是 mutual exclusion(互斥锁)在同一时刻Mutex 只允许一个线程来访问某些数据想要访问数据线程必须获取互斥锁(lock)lock数据结构是 mutex 的一部分它能跟着谁对数据拥有独占访问权。
mutext 通常被描述为通过锁来保护它所持有的数据
2.4.1.2、mutex的规则
使用数据之前必须尝试获取锁(lock)使用完 mutext 所保护的数据必须对数据进行解锁以便其他线程可以获取锁
2.4.1.3、MutexT的API
使用 Mutex::new(数据) 来创建 MutexMutex 是一个智能指针访问数据前通过 lock 方法来获取锁
会阻塞当前线程lock可能会失败返回的是 MutexGuard(智能指针实现了Deref 和 Drop)
use std::sync::Mutex;fn main() {let m Mutex::new(5);{let mut num m.lock().unwrap();*num 6;}println!(m {:?}, m);
}2.4.1.4、在多线程中共享 Mutext 与原子引用计数
use std::sync::Mutex;
use std::thread;fn main() {let counter Mutex::new(0);let mut handles vec![];for _ in 0..10 {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());
}该代码报错因为我们使用了move 移动了所有权所以其他线程使用会报错。
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 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());
}ArcT 原子引用计数
2.4.1.5、RefCell/RC和Mutex/ARC
Mutex 提供了内部可变性和 Cell 家族一样使用 Refcell 来 改变RC 里面的内容使用 Mutex 来改变 ARC 里面的内容
使用 Mutex 存在死锁风险
2.5、通过 Send Trait 和Sync Trait 来扩展并发
Rust 语言的并发特性比较少目前讲的并发特性都来自于标准库无需局限于标准库的并发可以自己实现并发。
在Rust里面有两个并发的概念
std::marker::Syncstd::marker::Send
2.5.1、Send 允许线程间转移所有权
Rust中几乎所有的类型都实现了 Send实现了 Send Trait 类型可以在线程间转移所有权但是RCT 没有实现 Send它只适用单线程场景。
任何完全由 Send 类型组成的类型也被标记为 Send。
除了原始指针之外几乎所有的基础类型都是 Send。
2.5.2、Sync 允许线程间转移所有权
实现 Sync 类型可以完全被多个线程引用如果 T 是 Sync那么 T 就是Send引用可以被安全的送往另个线程。
基础类型都是 Sync 完全由 Sync 类型组成的类型也是 Sync但是 RC不是Sync RefCell Cell 也不是SyncMutex 是 Sync
2.5.3、手动实现 Send 和 Sync是不安全的
总结
以上就是今天要讲的内容
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/911718.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!