我在crossbeam
中遇到了回收内存的问题。假设你正在实现一个简单的线程安全的无锁容器,它只保存一个值。任何线程都可以获得存储值的克隆,并且可以随时更新值,然后读者开始观察新值的克隆。带横梁的内存回收::时代
虽然典型的用例是指定像Arc<X>
为T,执行不能依靠T为指针大小 - 例如,X
可能是一个特点,导致脂肪指针Arc<X>
。但是,无锁地访问任意T似乎非常适合于epoch-based lock-free code。基于这些例子,我想出了这个:
extern crate crossbeam;
use std::thread;
use std::sync::atomic::Ordering;
use crossbeam::epoch::{self, Atomic, Owned};
struct Container<T: Clone> {
current: Atomic<T>,
}
impl<T: Clone> Container<T> {
fn new(initial: T) -> Container<T> {
Container { current: Atomic::new(initial) }
}
fn set_current(&self, new: T) {
let guard = epoch::pin();
let prev = self.current.swap(Some(Owned::new(new)),
Ordering::AcqRel, &guard);
if let Some(prev) = prev {
unsafe {
// once swap has propagated, *PREV will no longer
// be observable
//drop(::std::ptr::read(*prev));
guard.unlinked(prev);
}
}
}
fn get_current(&self) -> T {
let guard = epoch::pin();
// clone the latest visible value
(*self.current.load(Ordering::Acquire, &guard).unwrap()).clone()
}
}
当与一个不分配的类型一起使用时,例如,与T=u64
,它很好 - set_current
和get_current
可以被称为数百万次没有泄漏。 (过程监视器显示由于假想gc而导致的小内存振荡,如预期的那样,但没有长期增长。)但是,当T是分配的类型时,例如,人们可以很容易地观察到泄漏。例如:
fn main() {
use std::sync::Arc;
let c = Arc::new(Container::new(Box::new(0)));
const ITERS: u64 = 100_000_000;
let producer = thread::spawn({
let c = Arc::clone(&c);
move || {
for i in 0..ITERS {
c.set_current(Box::new(i));
}
}
});
let consumers: Vec<_> = (0..16).map(|_| {
let c = Arc::clone(&c);
thread::spawn(move || {
let mut last = 0;
loop {
let current = c.get_current();
if *current == ITERS - 1 {
break;
}
assert!(*current >= last);
last = *current;
}
})}).collect();
producer.join().unwrap();
for x in consumers {
x.join().unwrap();
}
}
运行此程序会显示内存使用量稳步大幅增加,最终消耗的内存量与迭代次数成正比。
根据the blog post introducing it,Crossbeam的时代回收“不运行析构函数,而只是释放内存”。 Treiber堆栈示例中的try_pop
使用ptr::read(&(*head).data)
将head.data
中包含的值移出目标为释放的head
对象。数据对象的所有权被转移给调用者,调用者可以将它移动到其他地方,或者当它超出范围时将其解除分配。
这将如何转化为上面的代码?设置者是guard.unlinked
的合适位置,还是如何确保drop
在底层对象上运行?取消明确drop(ptr::read(*prev))
的注释会导致失败的断言,检查单调性,可能表明过早的重新分配。
嗨Stjepan!我很高兴在这里见到你,特别是在这个非常具体的问题上,你可以看到“通常的嫌疑人”无法回答。如果你想聊天或需要任何东西,请不要犹豫在Rust聊天室中弹出:https://chat.stackoverflow.com/rooms/62927/rust –
谢谢,coco正是我所需要的 - 我现在添加了来自问题代码的答案已更新为可可。对于我来说,可可最终会被弃用并不重要,那么我会转回到Crossbeam。 – user4815162342
嗨,Matthieu,谢谢! :)我潜伏在房间里。同样,如果还有其他事情可以帮忙的话,请随时联系我。 –