Data structures 用铁锈手动锁紧

Data structures 用铁锈手动锁紧,data-structures,locking,rust,Data Structures,Locking,Rust,我正在尝试用Rust编写的实现。这在C语言中实现起来非常简单,但仍然需要进行复杂的运行时分析 我在获取Rust的互斥语义以允许迭代的手动锁定方面遇到困难。 这就是我现在的处境 首先,这是我希望在C中实现的结构的一部分的一个非常简单的实现: #include <stdlib.h> struct node { struct node * parent; }; struct node * create(struct node * parent) { struct node *

我正在尝试用Rust编写的实现。这在C语言中实现起来非常简单,但仍然需要进行复杂的运行时分析

我在获取Rust的互斥语义以允许迭代的手动锁定方面遇到困难。

这就是我现在的处境

首先,这是我希望在C中实现的结构的一部分的一个非常简单的实现:

#include <stdlib.h>

struct node {
  struct node * parent;
};

struct node * create(struct node * parent) {
  struct node * ans = malloc(sizeof(struct node));
  ans->parent = parent;
  return ans;
}

struct node * find_root(struct node * x) {
  while (x->parent) {
    x = x->parent;
  }
  return x;
}

int main() {
  struct node * foo = create(NULL);
  struct node * bar = create(foo);
  struct node * baz = create(bar);
  baz->parent = find_root(bar);
}
函数
change\u root
执行所有的重新设置父级,而函数
root
只是一个包装器,用于使用
find\u root
的结果重新设置根路径上节点的父级

为了在Rust中实现这一点,我决定必须使用
互斥体,而不仅仅是引用计数指针,因为
Rc
接口只允许在多个指向项的指针处于活动状态时通过写时复制进行可变访问。因此,所有代码都必须更改。在进入路径压缩部分之前,我挂断了
find\u root

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

struct Node {
    parent: Option<Arc<Mutex<Node>>>
}

fn create(parent: Option<Arc<Mutex<Node>>>) -> Node {
    Node {parent: parent.clone()}
}

fn find_root(x: Arc<Mutex<Node>>) -> Arc<Mutex<Node>> {
    let mut ans = x.clone();
    let mut inner = ans.lock();
    while inner.parent.is_some() {
        ans = inner.parent.clone().unwrap();
        inner = ans.lock();
    }
    ans.clone()
}
我想我需要的是手动锁定。对于路径A->B->C->…,我需要锁定A,锁定B,解锁A,锁定C,解锁B。。。当然,我可以打开所有的锁:锁A,锁B,锁C。。。解锁C,解锁B,解锁A,但这似乎效率低下

但是,
Mutex
不提供解锁,而是使用RAII如何在不直接调用
解锁


编辑:如评论所述,我可以使用
Rc
而不是
Arc
。这样做会导致相同的编译器错误

为了清楚地说明我试图通过使用手动锁定来避免什么,这里有一个
RefCell
版本,它可以编译,但在路径长度上使用了空格线性

fn find_root(x: Rc<RefCell<Node>>) -> Rc<RefCell<Node>> {
    let mut inner : RefMut<Node> = x.borrow_mut();
    if inner.parent.is_some() {
        find_root(inner.parent.clone().unwrap())
    } else {
        x.clone()
    }
}
fn查找_根(x:Rc)->Rc{
让mut-inner:RefMut=x.borrow_-mut();
如果inner.parent.is_some(){
查找_根(内部.parent.clone().unwrap())
}否则{
x、 克隆()
}
}

关于IRC,Jonathan Reem指出,
内部的
一直在借用,直到词法范围结束,这对我的要求来说太远了。内联生成以下内容,编译时不会出错:

fn find_root(x: Arc<Mutex<Node>>) -> Arc<Mutex<Node>> {
    let mut ans = x.clone();
    while ans.lock().parent.is_some() {
        ans = ans.lock().parent.clone().unwrap();
    }
    ans
}

编辑2:一次只能保存一个锁,racey也是如此。我仍然不知道如何进行手动锁定。

虽然不是您的字面问题(手动锁定)的答案,但使用加权并集和路径压缩的并集查找在Rust中非常简单:

fn unionfind<I: Iterator<(uint, uint)>>(mut iterator: I, nodes: uint) -> Vec<uint>
{
    let mut root = Vec::from_fn(nodes, |x| x);
    let mut rank = Vec::from_elem(nodes, 0u8);

    for (mut x, mut y) in iterator
    {
        // find roots for x and y; do path compression on look-ups
        while (x != root[x]) { root[x] = root[root[x]]; x = root[x]; }
        while (y != root[y]) { root[y] = root[root[y]]; y = root[y]; }

        if x != y
        {
            // weighted union swings roots
            match rank[x].cmp(&rank[y])
            {
                Less    => root[x] = y,
                Greater => root[y] = x,
                Equal   =>
                {
                    root[y] = x; 
                    rank[x] += 1 
                },
            }
        }
    }
}
fn unionfind(mut迭代器:I,节点:uint)->Vec
{
设mut root=Vec::from_fn(节点,| x | x);
让mut rank=Vec::from_elem(节点,0u8);
对于迭代器中的(mut x,mut y)
{
//查找x和y的根;在查找时进行路径压缩
而(x!=root[x]){root[x]=root[root[x]];x=root[x];}
而(y!=root[y]){root[y]=root[root[y]];y=root[y];}
如果x!=y
{
//加权并根
匹配秩[x]。cmp(&秩[y])
{
Less=>根[x]=y,
更大=>根[y]=x,
相等=>
{
根[y]=x;
秩[x]+=1
},
}
}
}
}

可能要点是联合查找算法可能不是处理节点所有权的最佳位置,通过使用对现有内存的引用(在本例中,仅使用节点的uint标识符),而不影响节点的生命周期,可以实现更简单的实现,当然,如果你能逃脱惩罚的话。

我相信这符合我们的标准

编辑#1

当进入n+1号锁的通道由n号锁保护时,它是否工作

如果你的意思是可以像下面这样,那么我认为答案是否定的

struct Level {
  data: bool,
  child: Option<Mutex<Box<Level>>>,
}
结构级{ 数据:布尔, 儿童:选择, }

然而,这是明智的,不应该起作用。当您将一个对象包装在互斥对象中时,表示“整个对象是安全的”。你不能同时说“整个馅饼都是安全的”和“我在吃皮下的东西”。也许您通过创建一个
互斥体
并锁定该互斥体来放弃安全性?

这仍然不是如何进行手动锁定的字面问题的答案,手动锁定只在并发设置中才重要(或者如果其他人强迫您使用
互斥体
对节点的引用)。相反,您似乎感兴趣的是如何使用
Rc
RefCell

RefCell
仅当持有一个可变引用时才允许可变写入。重要的是,
Rc
对象不是可变引用。它所讨论的可变引用是在
Rc
对象上调用
borrow_mut()
的结果,只要在有限的范围内(例如while循环的主体)这样做,就可以了

路径压缩中发生的重要事情是,当您将
节点的父指针摆动到
根节点时,
下一个
Rc对象将保持链的其余部分处于活动状态。然而,它不是这个词的铁锈意义上的参考

struct Node
{
    parent: Option<Rc<RefCell<Node>>>
}

fn find_root(mut node: Rc<RefCell<Node>>) -> Rc<RefCell<Node>>
{
    while let Some(parent) = node.borrow().parent.clone()
    {
        node = parent;
    }

    return node;
}

fn path_compress(mut node: Rc<RefCell<Node>>, root: Rc<RefCell<Node>>)
{
    while node.borrow().parent.is_some()
    {
        let next = node.borrow().parent.clone().unwrap();
        node.borrow_mut().parent = Some(root.clone());
        node = next;
    }
}
struct节点
{
家长:选项
}
fn find_root(mut node:Rc)->Rc
{
而let Some(parent)=node.borrow().parent.clone()
{
节点=父节点;
}
返回节点;
}
fn路径_压缩(多节点:Rc,根:Rc)
{
而node.borrow().parent.is_some()
{
让next=node.borrow().parent.clone().unwrap();
node.borrow_mut().parent=Some(root.clone());
节点=下一个;
}
}
这对我使用的测试工具来说运行良好,尽管可能仍然存在bug。当然,它编译和运行时不会出现
恐慌由于尝试
fn find_root(x: Arc<Mutex<Node>>) -> Arc<Mutex<Node>> {
    let mut ans = x.clone();
    loop {
        ans = {
            let tmp = ans.lock();
            match tmp.parent.clone() {
               None => break,
               Some(z) => z
            }
        }
    }
    ans
}
fn unionfind<I: Iterator<(uint, uint)>>(mut iterator: I, nodes: uint) -> Vec<uint>
{
    let mut root = Vec::from_fn(nodes, |x| x);
    let mut rank = Vec::from_elem(nodes, 0u8);

    for (mut x, mut y) in iterator
    {
        // find roots for x and y; do path compression on look-ups
        while (x != root[x]) { root[x] = root[root[x]]; x = root[x]; }
        while (y != root[y]) { root[y] = root[root[y]]; y = root[y]; }

        if x != y
        {
            // weighted union swings roots
            match rank[x].cmp(&rank[y])
            {
                Less    => root[x] = y,
                Greater => root[y] = x,
                Equal   =>
                {
                    root[y] = x; 
                    rank[x] += 1 
                },
            }
        }
    }
}
use std::sync::Mutex;

fn main() {
    // Create a set of mutexes to lock hand-over-hand
    let mutexes = Vec::from_fn(4, |_| Mutex::new(false));

    // Lock the first one
    let val_0 = mutexes[0].lock();
    if !*val_0 {
        // Lock the second one
        let mut val_1 = mutexes[1].lock();
        // Unlock the first one
        drop(val_0);
        // Do logic
        *val_1 = true;
    }

    for mutex in mutexes.iter() {
        println!("{}" , *mutex.lock());
    }
}
struct Level {
  data: bool,
  child: Option<Mutex<Box<Level>>>,
}
struct Node
{
    parent: Option<Rc<RefCell<Node>>>
}

fn find_root(mut node: Rc<RefCell<Node>>) -> Rc<RefCell<Node>>
{
    while let Some(parent) = node.borrow().parent.clone()
    {
        node = parent;
    }

    return node;
}

fn path_compress(mut node: Rc<RefCell<Node>>, root: Rc<RefCell<Node>>)
{
    while node.borrow().parent.is_some()
    {
        let next = node.borrow().parent.clone().unwrap();
        node.borrow_mut().parent = Some(root.clone());
        node = next;
    }
}
fn find_root(incoming: Arc<Mutex<Node>>) -> Arc<Mutex<Node>> {
    // We have to separate this from incoming since the lock must
    // be borrowed from incoming, not this local node.  
    let mut node = incoming.clone();
    let mut lock = incoming.lock();

    // Could use while let but that leads to borrowing issues.
    while lock.parent.is_some() {
       node = lock.parent.as_ref().unwrap().clone(); // !! uh-oh !!
       lock = node.lock();
    }

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

fn find_root(incoming: Arc<Mutex<Node>>) -> Arc<Mutex<Node>> {
    let mut node = incoming.clone();
    let mut handoff_node;
    let mut lock = incoming.lock();

    // Could use while let but that leads to borrowing issues.
    while lock.parent.is_some() {
       // Keep the data in node around by holding on to this `Arc`.
       handoff_node = node;

       node = lock.parent.as_ref().unwrap().clone();

       // We are going to move out of node while this lock is still around,
       // but since we kept the data around it's ok.
       lock = unsafe { mem::transmute(node.lock()) };
    }

    node
} 
struct Node {
    parent: Option<Rc<RefCell<Node>>>
}

fn find_root(node: Rc<RefCell<Node>>) -> Rc<RefCell<Node>> {
    let mut ans = node.clone(); // Rc<RefCell<Node>>
    loop {
        ans = {
            let ans_ref = ans.borrow(); // std::cell::Ref<Node>
            match ans_ref.parent.clone() {
                None => break,
                Some(z) => z
            }
        } // ans_ref goes out of scope, and ans becomes mutable
    }
    ans
}

fn path_compress(mut node: Rc<RefCell<Node>>, root: Rc<RefCell<Node>>) {
    while node.borrow().parent.is_some() {
        let next = {
            let node_ref = node.borrow();
            node_ref.parent.clone().unwrap()
        };
        node.borrow_mut().parent = Some(root.clone());
        // RefMut<Node> from borrow_mut() is out of scope here...
        node = next; // therefore we can mutate node
    }
}