生锈的无锁不安全数据共享总线



我对rust还比较陌生,我想使用Arc<>为许多线程提供对某些数据的不可变访问,但我希望Arc的原始所有者能够在不引入任何类型的锁(互斥锁、rwlock等(的情况下拥有写访问权。我的应用程序保证只有一个线程生成(写入(弧内的数据(因此永远不会有写入数据竞争到弧(。但是有许多线程正在读取弧中的数据(读取,而不是消耗(。我该如何实现?每当我靠近Arc并试图变异时,我都不被允许。显然,我知道这需要不安全。

总线被创建并克隆到读取器线程。

use std::borrow::BorrowMut;
use std::sync::Arc;
type Data<T> = Arc<Vec<T>>;
pub struct Bus<T> {
data: Data<T>
}
impl<T: Clone + Send + Sync + 'static> Bus<T> {
pub fn new() -> Self {
let data: Data<T> = Arc::new(Vec::new());

{
let mut data = data.clone();
std::thread::spawn(move || {
let a = data.borrow_mut();
a.push(value); // write to vector
}).join();
}
Bus {
data
}
}
pub fn read(&self, idx: usize) -> T {
self.data[idx].clone()
}
}

给出:

不能将Arc中的数据作为可变借用

免责声明-并发数据结构很难,我不是方面的专家


您得到的错误消息本质上是说";您不能更改Arc的内容,因为当您更改它时,其他人可能正在读取它,从而导致数据竞争。

典型的解决方案是使用Arc<Mutex<T>>Mutex允许您通过首先锁定互斥锁来从不可变引用转换为可变引用。

但是,您已经表示不想使用锁定。

因此,如果Mutex(以及类似RwLock的东西(不在表中,您将需要一个类似Vec的数据结构,它允许通过不可变引用进行突变

然而,我的理解是,通常没有什么理由更喜欢";并发的";或";"无锁定";Vec而不是简单的Arc<Mutex<Vec<T>>(或者在您的情况下可能是Arc<RwLock<T>>(。总的来说,性能将是相似的。

如果你想进一步阅读,这个reddit线程可以深入了解为什么这样的数据结构并不真正存在。

如果您发现该模型也是合适的,那么也有并发的类似HashMap的结构。映射通常更适合这种无锁或高度并发的工作负载。

例如,chashmap使用每个bin锁定(即映射中的每个bin都有自己的锁(,因此锁争用保持相对较低。

还有evmap,它是完全无锁的,但代价是最终保持一致并使用大约2倍的内存。它基本上维护了你的地图的两份副本,其中一份是";读取地图";另一个是";写地图";。然后你可以"发布";应用写入的更改。

否,如果一个Vec<T>由一个线程编写,并由多个线程读取,那么这是一个数据竞赛。

还有其他一些免锁定选项,但它们都与您的示例不同。

  • 如果T是一个简单类型(普通整数类型或具有适当长度并实现Copy特征的类型(,并且Vec长度是固定的,则可以使用固定长度数组+原子类型。您可以安全地读取和写入每个元素,但不能增长数组。

    Crateatomic提供了一个Atomic<T>类型,您可以在编译时检查它是否是无锁的。

  • AFAIK,无锁Vec是不可能的,但是一些板条箱提供无锁队列。但很明显,索引无法访问队列。

最新更新