我找到了this article,但它看起来不对,因为NNNNNNNNNNNNNNNNYYYYYYYYYYYNNNNNYYYYYYYYYYYYYYYYNNNNNNNNNNNYYYYYYYYYYNNN
无法保证锁定下的Cell
与锁定set()
之间的同步。
get()
会影响其他非原子写操作吗?
我尝试用Atomic_.store(true, Ordering::Release)
编写它,它看起来接近Java风格,但它失败了。在这种情况下,我无法找到正确使用AtomicPtr
的示例。
答案 0 :(得分:12)
Atomic_.store(true, Ordering::Release)
会影响其他非原子写操作吗?
是
实际上,Ordering
存在的主要原因是对非原子读写强加一些排序保证:
<强>宽松强>
约束越少Ordering
;唯一不能重新排序的操作是对相同原子值的操作:
atomic.set(4, Ordering::Relaxed);
other = 8;
println!("{}", atomic.get(Ordering::Relaxed));
保证打印4
。如果另一个帖子读到atomic
为4
,则无法保证other
是否为8
。
<强>推出/获取强>
分别写下和阅读障碍:
store
操作一起使用,并保证执行先前的写入,load
操作一起使用,并保证进一步读取的值至少与相应store
之前写入的值一样新鲜。< / LI>
所以:
// thread 1
one = 1;
atomic.set(true, Ordering::Release);
two = 2;
// thread 2
while !atomic.get(Ordering::Acquire) {}
println!("{} {}", one, two);
保证one
为1
,并且对two
一无所知。
请注意,加载Relaxed
的{{1}}商店或加载Acquire
的{{1}}商店基本上没有意义。
请注意,Rust提供Release
:对于商店,它的行为为Relaxed
,对于加载,它的行为为AcqRel
,因此您不必记住哪个是...我不建议但是,因为所提供的保证是如此不同。
<强> SeqCst 强>
最具约束力的Release
。保证一次性跨所有线程排序。
在Rust中编写双重检查锁定的正确方法是什么?
因此,双重检查锁定就是利用这些原子操作来避免在不必要时锁定。
想法是有3件:
并使用它们:
困难在于确保非原子读/写正确排序(并以正确的顺序显示)。从理论上讲,你需要完全围栏;在实践中遵循C11 / C ++ 11内存模型的习惯用法就足够了,因为编译器必须才能使它工作。
让我们先检查一下代码(简化):
Acquire
有3个原子操作,通过注释编号。我们现在可以检查内存排序的哪种保证,每个必须提供正确性。
(1)如果为true,则返回对该值的引用,该引用必须引用有效内存。这要求在原子变为真之前执行对该存储器的写入,并且只有在该存储器为真之后才执行该存储器的读取。因此(1)需要Ordering
和(3)需要struct Lazy<T> {
initialized: AtomicBool,
lock: Mutex<()>,
value: UnsafeCell<Option<T>>,
}
impl<T> Lazy<T> {
pub fn get_or_create<'a, F>(&'a self, f: F) -> &'a T
where
F: FnOnce() -> T
{
if !self.initialized.load(Ordering::Acquire) { // (1)
let _lock = self.lock.lock().unwrap();
if !self.initialized.load(Ordering::Relaxed) { // (2)
let value = unsafe { &mut *self.value.get() };
*value = Some(f(value));
self.initialized.store(true, Ordering::Release); // (3)
}
}
unsafe { &*self.value.get() }.as_ref().unwrap()
}
}
。
(2)没有这样的约束,因为锁定Acquire
相当于一个完整的内存屏障:所有写入都保证在之前发生,所有读取只发生在之后。因此,此负载无需进一步保证,因此Release
是最优化的。
因此,就我而言,这种双重检查的实施在实践中看起来是正确的。
为了进一步阅读,我真的推荐the article by Preshing,它链接在您链接的文章中。它显着突出了理论(围栏)和实践(原子载荷/商店被降低到围栏)之间的差异。