RUST标准库channel模块Queue源代码分析

本文摘自《深入RUST标准库》,已经全网发售,恳请支持

RUST标准库中mpsc模块有一个用于多线程发,单线程收的Queue实现,极其精巧,值得学习及牢记

//以下是简单的FIFO的队列实现
pub enum PopResult<T> {
    //返回队列成员
    Data(T),
    //队列为空
    Empty,
    //队列的一致性出错
    Inconsistent,
}

//节点结构
struct Node<T> {
    //next指针,利用原子指针实现多线程的Sync,值得牢记
    next: AtomicPtr<Node<T>>,
    value: Option<T>,
}

///  能够被多个线程操作的队列
pub struct Queue<T> {
    //利用原子指针操作实现多线程的Sync,极大简化了代码
    head: AtomicPtr<Node<T>>,
    //从后面的代码看,这里实际上是队列的头部,这个Queue的代码搞得奇怪
    tail: UnsafeCell<*mut Node<T>>,
}

unsafe impl<T: Send> Send for Queue<T> {}
unsafe impl<T: Send> Sync for Queue<T> {}

impl<T> Node<T> {
    unsafe fn new(v: Option<T>) -> *mut Node<T> {
        //申请堆内存后,将堆内存的指针提取出来
        Box::into_raw(box Node { next: AtomicPtr::new(ptr::null_mut()), value: v })
    }
}

impl<T> Queue<T> {
    pub fn new() -> Queue<T> {
        let stub = unsafe { Node::new(None) };
        //生成一个空元素的节点列表
        Queue { head: AtomicPtr::new(stub), tail: UnsafeCell::new(stub) }
    }

    //在头部
    pub fn push(&self, t: T) {
        unsafe {
            let n = Node::new(Some(t));
            //换成C的话,就是head->next = n; head = n
            //对于空队列来说,是tail = head; head->next = n; head = n; 
            //现在tail实际上是队列头部,head是尾部。tail的next是第一个有意义的成员 
            let prev = self.head.swap(n, Ordering::AcqRel);
            //要考虑在两个赋值中间加入了其他线程的操作是否会出问题,
            //这里面有一个复杂的分析,
            //假设原队列为head, 有两个线程分别插入新节点n,m
            //当n先执行,而m在这个代码位置插入,则m插入前prev_n = pre_head, head = n
            //m插入后,prev_m = n, head = m。如果n先执行下面的语句,执行完后 
            // pre_head->next = n, n->next = null,然后m执行完下面语句
            // pre_head->next = n, n->next = m, head = m,队列是正确的。
            // 如果m先执行,执行完后 pre_head->next = null, n->next = m, head = m;
            // 然后n执行,执行完成后 pre_head->next = n, n->next = m, head =m, 队列是正确的。
            // 换成多个线程实际上也一样是正确的。这个地方处理十分巧妙,这是系统级编程语言的魅
            //力, 当然,实际上是裸指针编程的魅力            
            (*prev).next.store(n, Ordering::Release);
            
        }
    }

    //仅有一个线程在pop
    pub fn pop(&self) -> PopResult<T> {
        unsafe {
            //tail实际上是队列头,value是None
            let tail = *self.tail.get();
            //tail的next是第一个有意义的成员
            let next = (*tail).next.load(Ordering::Acquire);

            //next如果为空,说明队列是空队列
            if !next.is_null() {
                //此处原tail会被drop,tail被赋成next
                //因为push只可能改变next,所以这里不会有线程冲突问题
                //这个语句完成后,队列是完整及一致的 
                *self.tail.get() = next;
                assert!((*tail).value.is_none());
                assert!((*next).value.is_some());
                //将value的所有权转移出来,*next的value又重新置为None
                //当tail == head的时候 就又都是stub了
                let ret = (*next).value.take().unwrap();
                //恢复Box,以便以后释放堆内存
                let _: Box<Node<T>> = Box::from_raw(tail);
                return Data(ret);
            }

            //判断是否出错
            if self.head.load(Ordering::Acquire) == tail { Empty } else { Inconsistent }
        }
    }
}

impl<T> Drop for Queue<T> {
    fn drop(&mut self) {
        unsafe {
            //空队列的stub也要释放
            let mut cur = *self.tail.get();
            while !cur.is_null() {
                let next = (*cur).next.load(Ordering::Relaxed);
                //恢复Box并消费掉,释放堆内存
                let _: Box<Node<T>> = Box::from_raw(cur);
                cur = next;
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
如果需要在没有网络连接的环境下进行 Rust 内核开发,可以使用离线安装的方式,下面是具体步骤: 1. 下载 Rust 工具链 首先,在有网络连接的环境下,需要下载 Rust 工具链的离线安装包。可以从 Rust 官网下载对应平台的离线安装包:https://www.rust-lang.org/tools/install 下载完成后,将安装包保存到本地。 2. 安装 Rust 工具链 将 Rust 工具链的离线安装包拷贝到目标机器上,执行安装命令: ``` ./rustup-init --no-modify-path -y --default-toolchain none ``` 这将会安装 Rust 工具链,并且不会修改环境变量 PATH。 3. 配置 Rust 工具链 为了构建内核,需要使用 `xargo` 工具,因此需要将 Rust 工具链配置为使用 `xargo` 工具。可以使用以下命令配置 Rust 工具链: ``` rustup toolchain link kernel /path/to/rustc --no-self-update --force ``` 其中,`/path/to/rustc` 是 Rust 工具链的安装路径,可以使用绝对路径或相对路径,`kernel` 是自定义的 Rust 工具链名称。 4. 安装 `xargo` 工具 `xargo` 是一个 Rust 工具,可以用于构建 Rust 内核。使用以下命令安装 `xargo` 工具: ``` cargo install --force --path /path/to/xargo ``` 其中,`/path/to/xargo` 是 `xargo` 工具的源代码路径。 5. 安装 Rust 标准库 默认情况下,Rust 工具链是不包含 Rust 标准库的,需要手动安装。可以使用以下命令安装 Rust 标准库: ``` rustup component add rust-src --toolchain kernel ``` 其中,`--toolchain` 参数指定了要安装的 Rust 工具链。如果没有指定该参数,默认会使用当前的 Rust 工具链。 安装完成后,就可以使用 `xargo` 工具进行构建了,例如: ``` xargo build --target x86_64-unknown-none --release --no-default-features --features myfeature ``` 其中,`--target` 参数指定了目标平台,`x86_64-unknown-none` 表示 x86_64 架构的裸机环境。`--release` 参数表示使用优化编译。`--no-default-features` 参数表示不使用 Rust 标准库的默认特性,`--features myfeature` 参数可以指定使用特定的 Rust 标准库特性。 以上就是在离线环境下配置 Rust 工具链、`xargo` 工具、Rust 标准库的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任成珺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值