Rust权威指南之无畏并发

2022-12-29 0 379

一.详述

安全可靠因此高效率地处置mammalian程式设计是Rust的另两个主要就最终目标。mammalian程式设计和博戈达程式设计这三种基本概念随著Bijnor的多核强化而显得愈来愈关键。mammalian程式设计容许流程中的相同部份互相分立地运转;博戈达程式设计则容许流程中相同部份与此同时继续执行。

二.缓存的建立

在大部份那时作业系统中,继续执行流程的标识符会运转在民主化中,作业系统会与此同时管理工作数个民主化。类似于地,流程外部也能保有数个与此同时运转的分立部份,用以运转那些分立部份的就叫作缓存。

虽然多缓存能与此同时运转,大部份将城内排序操作方式分拆至数个缓存能提升操控性。但这也减少了流程的维数,即使相同缓存的继续执行次序是难以确认的。这可能会引致一连串的难题:

当数个缓存以不完全一致的次序出访统计数据或天然资源时造成的市场竞争状况

只会出那时某一情况下且难以平衡再现和复原的Bug

上面让他们看呵呵国际标准复本缓存API的采用

2.1.采用spawn建立新缓存

他们能调用thread::spawn函数来建立缓存,它拒绝接受两个旋量群做为模块,该旋量群会包涵他们想在新缓存中运转的标识符。

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::frommillis(1))

}

});

for i in 1..5{

println!(“hi number{} from the main thread!”, i);

thread::sleep(Duration::frommillis(1))

}

}

这里需要注意当主缓存运转结束之后,建立出来的新缓存也会相应的停止,而不管它的打印任务是否完成。输出如下:

hi number1 from the main thread!

hi number1 from the spawned thread!

hi number2 from the main thread!

hi number2 from the spawned thread!

hi number3 from the main thread!

hi number3 from the spawned thread!

hi number4 from the main thread!

hi number4 from the spawned thread!

hi number5 from the spawned thread!

2.2.采用Join句柄等待大部份缓存结束

在上面的例子中他们也能看到两个难题,新缓存的统计数据还没有继续执行完毕就即使main函数继续执行结束而结束。这并不是他们希望的,上面他们看呵呵采用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::frommillis(1))

}

});

for i in 1..5{

println!(“hi number{} from the main thread!”, i);

thread::sleep(Duration::frommillis(1))

}

handle.join().unwrap()

}

但他们也是需要注意,join函数采用的位置不当也会有难题的,如下:

let handle = thread::spawn({

for i in 1..10{

println!(“hi number{} from the spawned thread!”, i);

thread::sleep(Duration::frommillis(1))

}

});

handle.join().unwrap();

for i in 1..5{

println!(“hi number{} from the main thread!”, i);

thread::sleep(Duration::frommillis(1))

}

此时输出结果如下

hi number1 from the spawned thread!

hi number2 from the spawned thread!

hi number3 from the spawned thread!

hi number4 from the spawned thread!

hi number5 from the spawned thread!

hi number6 from the spawned thread!

hi number7 from the spawned thread!

hi number8 from the spawned thread!

hi number9 from the spawned thread!

hi number1 from the main thread!

hi number2 from the main thread!

hi number3 from the main thread!

hi number4 from the main thread!

2.3.在缓存中采用move旋量群

move旋量群常常被用以thread::spawn函数配合采用,它容许你在某个缓存中采用以自另两个缓存的统计数据。

当他们为了采用主缓存中统计数据,新缓存的旋量群必须捕获它所需要的值。例子:

use std::thread;

fn main(){

let v = vec![1,2,3];

let handle = thread::spawn(move {

println!(“Heres a vector:{:?}”, v);

});

// println!(“{:?}”, v);@1

handle.join().unwrap();

}

fter move错误。

三.缓存间消息传递

采用消息传递机制来保证并安全可靠正在显得愈来愈流行。在这种机制中,缓存或actor之间通过给彼此发送包涵统计数据的消息来进行通信。

Go程式设计语言在处置mammalian程式设计的口号:不要通过共享内存来通信,而是通过通信来共享内存。

Rust在国际标准复本实现了两个名为通道的程式设计基本概念,它能被用于实现基于消息传递的mammalian机制。程式设计中的通道由发送者和接收者两部份组成。某一处标识符能通过调用发送

use std::sync::mpsc;

use std::thread;

fn main(){

// tx: Sender发送端

// rx: Receiver接收端

let (tx, rx)= mpsc::channel();

//新缓存发送统计数据

thread::spawn(move {

let val = String::from(“hello”);

// val 发送到通道之内会发生大部份权转移

tx.send(val).unwrap();

// println!(“val =>{}”, val); ERROR value borrowed here after move

});

// main函数拒绝接受

let received = rx.recv().unwrap();

println!(“收到消息:{}”, received);

}

cv。他们采用的recv会阻塞主缓存的继续执行直到有值被传入通道。一旦有值被传入通道,recv就会将它包裹在Result中返回。而如果通道的发送端全部关闭了,recv则会返回两个错误来表明当前通道再也没有可拒绝接受的值。tryrecv方法不会阻塞缓存,它会立即返回Result:当通道中存在消息时,返回包涵该消息的Ok变体;否则便返回Err变体。当某个缓存需要一边等待消息一边完成其他工作时,tryrecv方法会非常有用。他们能采用循环对消息进行处置。

3.1.发送数个值

上面他们演示呵呵新缓存中将数个值发送到通道,在主缓存中将rx视作迭代器,接收通道中的值并打印。

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(“hello”),

String::from(“world”),

String::from(“the”),

String::from(“thread”),

String::from(“haha”),

];

//遍历往通道中发送统计数据

vals.intoiter().foreach(item {

tx.send(item).unwrap();

//暂停一秒钟

thread::sleep(Duration::fromsecs(1));

});

});

//遍历拒绝接受统计数据

for item in rx {

println!(“收到消息:{}”, item);

}

}

此时继续执行标识符,他们能看到主缓存中的for循环中继续执行暂停或延迟指令,这也就表明主缓存确实是在等待接收新缓存中传递过来的值。输出结果如下:

收到消息: hello

收到消息: world

收到消息: the

收到消息: thread

收到消息: haha

3.2.多生产者

上面他们通过克隆通道的发送端来建立数个能够发送值到同两个接收端的缓存。实现数个生产者和单个消费者的模式。

use std::sync::mpsc;

use std::thread;

use std::time::Duration;

fn main(){

let (tx, rx)= mpsc::channel();

let tx1= mpsc::Sender::clone(&tx);

thread::spawn(move {

let vals = vec![

String::from(“tx1=> hi”),

String::from(“tx1=>from”),

String::from(“tx1=> the”),

String::from(“tx1=> thread”),

];

for val in vals {

tx1.send(val).unwrap();

thread::sleep(Duration::fromsecs(1));

}

});

thread::spawn(move {

let vals = vec![

String::from(“tx => hello”),

String::from(“tx => world”),

String::from(“tx => haha”),

];

vals.intoiter().foreach(item {

tx.send(item).unwrap();

thread::sleep(Duration::fromsecs(1));

});

});

//消费统计数据

for item in rx {

println!(“收到消息:{}”, item);

}

}

四.共享状况的mammalian

消息传递确实是一种不错mammalian通信机制,但它并不是唯一的解决方法,上面他们看呵呵如何通过共享内存来通信。

从某种程度来说,任何程式设计语言中的通道都有类似于于单一大部份权的基本概念,即使你不应该在值传递给通道之后在采用它。而基于共享内存的mammalian通信机制则更类似于于多重大部份权的基本概念:数个缓存能与此同时出访相同的内存地址。但虽然要管理工作数个大部份者,大部份这会减少额外的复杂性。Rust的类型系统和大部份权规能够帮助他们正确的管理工作那些大部份权。

4.1.互斥体

互斥是共享内存领域比较常见的mammalian原语。两个互斥体在任意时刻只允

在采用互斥体的时候需要注意上面两点:

上面他们实现两个数个缓存累加排序结果的例子:

use std::sync::{Arc, Mutex};

use std::thread;

fn main(){

let counter = Arc::new(Mutex::new(0));//@1

let mut handlers = vec![];

for in 0..10{

let counter = Arc::clone(&counter);//@2

let handle = thread::spawn(move {

let mut num = counter.lock().unwrap();//@3

*num +=1;

});

handlers.push(handle);

}

for handle in handlers {

handle.join().unwrap();

}

println!(“排序结果:{}”,*counter.lock().unwrap());//@4排序结果:10

}

上面他们对例子中关键的标识符解释下:

针对@1的标识符,首先M两个智能指针方便后续采用的时候进行引用克隆,此时他们第两个念头肯定想的是上一节学习的Rc,但Rc并不是缓存安全可靠的,Rust提供了两个安全可靠的计数引用的智能指针Arc。总结来说就是采用Arc包裹Mutex来实现多缓存共享大部份权;

对于@2的标识符是这样的,对于引用进行安全可靠复制(引用计数加一),将其大部份权转移到指定的缓存中;

据的可变引用,此时他们就能继续执行加一操作方式了;

4.2.注意点

ll系列类型有着相似的功能,它同样提供了外部可变性。

另外还有一点需要注意,Rc会造成循环引用的风险。两个Rc值在互相指向对方时会造成内存泄漏。与之类似于,采用Mutex也会有造成死锁的风险。

五.mammalian扩展

采用Sync trait和Send trait对mammalian进行扩展。

只有实现了Send trait的类型才能安全可靠地在缓存间转移大部份权,除了Rc等极少数的类型,几乎大部份的Rust类型都实现了Send trait。任何完全由Send类型组成的复合类型都会自动标记为Send,几乎大部份的原生类型都满足Send约束。

只有实现了Sync trait的类型才能安全可靠地被数个缓存引用。换句话说,对于任何类型T,如果&T满足约束Send,那么T就是满足Sync的。这意味T的引用能够安全可靠地传递至另外的缓存中。与Send类似于,大部份原生类型都满足Sync约束,而完全满足Sync的类型组合的复合类型也都会被自动识别为满足Sync的类型。

Rust权威指南之无畏并发

最后需要注意手动实现Send和Sync是不安全可靠的,另外Send和Sync甚至没有任何可供实现的方法。它们仅仅被用以强化与mammalian相关的不可变性。

————————————————

版权声明:本文为CSDN博主lucky.麒麟的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/yhflyl/article/details/128347607

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务