rust crossbeam学习

crossbeam是好东西,简化了rust并发编程,支持无锁编程,对性能要求高的程序可以尝试。

crossbeam-AtomCell

线程安全的单值操作,和std的Cell相似,std的cell对并发不友好,AtomCell并发情况下也可保证内存安全

基本用法

use crossbeam::atomic::AtomicCell;
use crossbeam::scope;

fn main() {
    let value = AtomicCell::new(0u8); //创建一个新的Cell,初始值为0,类型为u8

    scope(|s| {
        let thread1 = s.spawn(|_| {
            for _ in 0..10 {
                value.fetch_add(1); // +1 10次
            }
        });

        let thread2 = s.spawn(|_| {
            for _ in 0..5 {
                value.fetch_sub(1); // -1 5次
            }
        });

        thread1.join().unwrap();
        thread2.join().unwrap();
    })
    .unwrap();

    let final_value = value.load(); // load 获取cell的值
    println!("Final value: {}", final_value);
}
//结果: Final value: 5

crossbeam-channel

比std::sync::mpsc更好的MPSC(多生产者、单消费者)队列,性能更好,功能更多、支持select!(友好易用)

基础示例

use crossbeam::channel::unbounded;

fn main(){
    let (tx, rx) = unbounded(); // 新建一个无界channel
    tx.send(1).unwrap();  // 发送数字1到channel
    let recv: i32 = rx.recv().unwrap();   // 从channel中收取信息,如果没有信息则阻塞
    println!("{}", recv);
}
// 1

无界channel和有界channel

bounded: 有界channel,声明时需要加上channel的容量,e.g. 声明一个容纳10个对象的channel:// let s, r = bounded(10);
unbounded: 无界channel,channel容量会根据数据自动调整,在编写系统级应用时个人建议采用有界channel(感觉某些情况下内存不受控)

有界channel的用法

声明一个有界channel

use crossbeam::channel::bounded;

fn main(){
    let (tx, rx) = bounded(2);
    tx.send(1).unwrap();
    let recv: i32 = rx.recv().unwrap();
    println!("{}", recv);
}

当channel容量已满时,继续插入数据会阻塞直到channel数据被接收

use crossbeam::channel::bounded;

fn main(){
    let (tx, rx) = bounded(2);
    tx.send(1).unwrap();
    println!("{}",tx.len());
    tx.send(2).unwrap();
    println!("{}",tx.len());
    tx.send(3).unwrap();    // 这里会阻塞,因为channel容量为2,当前channel已满,如果前面插入的数据未消费是无法继续插入数据的
    println!("{}",tx.len());
    let recv: i32 = rx.recv().unwrap();
    println!("{}", recv);
}

channel在多线程复用时需要clone

下面是官方示例:

use crossbeam_channel::unbounded;

let (s1, r1) = unbounded();
let (s2, r2) = (s1.clone(), r1.clone());
let (s3, r3) = (s2.clone(), r2.clone());

s1.send(10).unwrap();
s2.send(20).unwrap();
s3.send(30).unwrap();

assert_eq!(r3.recv(), Ok(10));
assert_eq!(r1.recv(), Ok(20));
assert_eq!(r2.recv(), Ok(30));

多线程模式下的channel使用方法

感觉平常用不上,引用官方示例:

use crossbeam_channel::bounded;
use crossbeam_utils::thread::scope;

let (s, r) = bounded(0);

scope(|scope| {
    // Spawn a thread that receives a message and then sends one.
    scope.spawn(|_| {
        r.recv().unwrap();
        s.send(2).unwrap();
    });

    // Send a message and then receive one.
    s.send(1).unwrap();
    r.recv().unwrap();
}).unwrap();

关闭channel

use crossbeam_channel::{unbounded, RecvError};

let (s, r) = unbounded();
s.send(1).unwrap();
s.send(2).unwrap();
s.send(3).unwrap();

// 关闭channel的发送端
drop(s);

// 接收端还能接受剩余的数据
assert_eq!(r.recv(), Ok(1));
assert_eq!(r.recv(), Ok(2));
assert_eq!(r.recv(), Ok(3));

// 数据收完后channel就为空
assert!(r.is_empty());

// 这时候r.recv不会阻塞
// 会直接返回 `Err(RecvError)` 
assert_eq!(r.recv(), Err(RecvError));

好用的Select

好用的东西,通常放在loop中,用来处理消息等非常方便
定义几个channel操作,那个操作有消息就走哪个那个逻辑,否则就走default或者一直阻塞
比如:

loop {
	select
		通道A => 处理A
		通道B => 处理B
	}

引用官方示例:

use std::thread;
use std::time::Duration;
use crossbeam_channel::{select, unbounded};

let (s1, r1) = unbounded();  // 创建两个无界channel
let (s2, r2) = unbounded();

thread::spawn(move || s1.send(10).unwrap());  // 异步发送消息到channel
thread::spawn(move || s2.send(20).unwrap());

// At most one of these two receive operations will be executed.
select! {
    recv(r1) -> msg => assert_eq!(msg, Ok(10)),  // 根据不同的channel收到数据做不同的处理
    recv(r2) -> msg => assert_eq!(msg, Ok(20)),
    default(Duration::from_secs(1)) => println!("timed out"),  // 如果所有channel都没有收到数据就走default,1秒后打印time out
}

select的扩展方法

  • after 创建一个多长时间以后会插入一个消息的channel(做延迟发送)
  • tick 创建一个定时发送消息的channel(定时器)
  • never 创建一个从不发送消息的channel

用法示例(还是引用官方示例,比较简单):

use std::time::{Duration, Instant};
use crossbeam_channel::{after, select, tick};

let start = Instant::now();
let ticker = tick(Duration::from_millis(50));
let timeout = after(Duration::from_secs(1));

loop {
    select! {
        recv(ticker) -> _ => println!("elapsed: {:?}", start.elapsed()),
        recv(timeout) -> _ => break,
    }
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值