网站优化快速排名软件,网站建设控制面板怎么设置,58企业网站怎么做,做体育设施工程公司的网站Rust 标准库只提供了 1:1 线程模型 Rust 是较为底层的语言#xff0c;如果愿意牺牲性能来换取抽象#xff0c;以获得对线程运行更精细的控制及更低的上下文切换成本#xff0c;使用实现了 M:N 线程模型的 crate
示例
use std::thread;
use std::time::Duration;fn main() …Rust 标准库只提供了 1:1 线程模型 Rust 是较为底层的语言如果愿意牺牲性能来换取抽象以获得对线程运行更精细的控制及更低的上下文切换成本使用实现了 M:N 线程模型的 crate
示例
use std::thread;
use std::time::Duration;fn main() {// 调用 thread::spawn 函数并传递一个闭包来创建线程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));}// 等待结束// // unwrap 在接收到 None 时将返回 panichandle.join().unwrap();
}在一个线程中使用另一个线程的数据
在参数列表前使用 move 关键字强制闭包获取其使用的环境值的所有权。 这个技巧在创建新线程将值的所有权从一个线程移动到另一个线程时最为实用
use std::thread;fn main() {let v vec![1, 2, 3]let handle thread::spawn(|| {// v is borrowed here// Rust 不知道这个新建线程会执行多久所以无法知晓 v 的引用是否一直有效println!(Heres a vector: {:?}, v);});// 一个具有闭包的线程尝试使用一个在主线程中被回收的引用 v// drop(v);handle.join().unwrap();
}force the closure to take ownership of v (and any other referenced variables), use the move keyword
use std::thread;fn main() {let v vec![1, 2, 3];// 增加 move 关键字强制闭包获取其使用的值的所有权let handle thread::spawn(move || {println!(Heres a vector: {:?}, v);});handle.join().unwrap();// value borrowed here after moveprintln!(Heres a vector: {:?}, v); // 这里编译报错
}use std::thread;fn main() {let v vec![1, 2, 3]let handle thread::spawn(move || {// v is borrowed hereprintln!(Heres a vector: {:?}, v);});handle.join().unwrap();
}channel
mpscmultiple producer, single consumer
基本用法
use std::thread;
// 多发单收模型
// multiple producer, single consumer
use std::sync::mpsc;fn main() {// tx 是发送端rx是接收端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);
}try_recv 不会阻塞相反它立刻返回一个 ResultT, EOk 值包含可用的信息而 Err 值代表此时没有任何消息
示例
use std::thread;
use std::sync::mpsc;
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);}
}所有权被转移
use std::thread;
use std::sync::mpsc;fn main() {let (tx, rx) mpsc::channel();thread::spawn(move || {let val String::from(hi);tx.send(val).unwrap();// 编译失败// value borrowed here after moveprintln!(val is {}, val);});let received rx.recv().unwrap();println!(Got: {}, received);
}多生产者
通过克隆通道的发送端来做到 类似于Go中的“不要通过共享内存来通讯而是通过通讯来共享内存 “Do not communicate by sharing memory; instead, share memory by communicating.”
use std::thread;
use std::sync::mpsc;
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);}println!(finish)
}Mutex 互斥器
使用方式
cat main.rs
use std::sync::Mutex;fn main() {let m Mutex::new(5);{// lock 调用 返回 一个叫做 MutexGuard 的智能指针// 这个智能指针实现了 Deref 来指向其内部数据// 也提供了一个 Drop 实现当 MutexGuard 离开作用域时自动释放锁let mut num m.lock().unwrap();*num 6;}// 打印内容如下 Mutex { data: 6, poisoned: false, .. }println!(m {:?}, m);
}糟糕例子
use std::sync::Mutex;
use std::thread;fn main() {let counter Mutex::new(0);let mut handles vec![];for _ in 0..10 {// value moved into closure here, in previous iteration of loop// 编译失败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());
}解决方式-Arc
Arc 和 Rc 有着相同的 API Arc 一个类似 Rc 并可以安全的用于并发环境的类型 字母 “a” 代表 原子性atomic所以这是一个原子引用计数
为什么不是所有标准库中的类型都默认使用 Arc 实现 线程安全带有性能惩罚只在必要时才为此买单
use std::sync::{Mutex, Arc};
use std::thread;fn main() {// counter 是不可变的因为前面没有mut// 意味着MutexT 提供了内部可变性// 就像使用 RefCellT 可以改变 RcT 中的内容那样// 使用 MutexT 来改变 ArcT 中的内容 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();}// 打印结果为10println!(Result: {}, *counter.lock().unwrap());
}Mutex 也有造成 死锁deadlock 的风险
使用 Sync 和 Send trait 的可扩展并发
语言本身对并发知之甚少。上述讨论的几乎所有内容都属于标准库而不是语言本身的内容可以编写自己的或使用别人编写的并发功能。 但有两个并发概念是内嵌于语言中的std::marker 中的 Sync 和 Send trait
通过 Send 允许在线程间转移所有权
【Send 标记 trait】 表明类型的所有权可以在线程间传递 几乎所有的 Rust 类型都是Send 的但是也有例外比如Rc 如果克隆了 Rc 的值并尝试将克隆的所有权转移到另一个线程这两个线程都可能同时更新引用计数Rc 被实现为用于单线程场景所以不用担心多线程下的引用计数问题。
糟糕例子
the trait Send is not implemented for RcMutexi32
use std::rc::Rc;
use std::sync::Mutex;
use std::thread;fn main() {let counter Rc::new(Mutex::new(0));let mut handles vec![];// RcMutexi32 cannot be sent between threads safelyfor _ in 0..10 {let counter Rc::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());
}解决办法
使用原子引用计数 Arc
Sync 允许多线程访问
【Sync 标记 trait】 一个实现了 Sync 的类型可以安全的在多个线程中拥有其值的引用 对于任意类型 T如果 TT 的引用是 Send 的话 T 就是 Sync 的 其引用就可以安全的发送到另一个线程
Rc 不是 Sync的 RefCell 和 Cell 系列类型不是 Sync 的 RefCell 在运行时所进行的借用检查也不是线程安全的 Mutex 是 Sync 的可以被用来在多线程中共享访问