我找到了this article,但它看起来错了,因为Cell
不能保证锁下set()
和锁上get()
之间的同步.
非写Atomic_.store(true, Ordering::Release)
会影响其他原子操作吗?
我试着用AtomicPtr
来写它,看起来很像Java风格,但失败了.我找不到在这种情况下正确使用AtomicPtr
的例子.
我找到了this article,但它看起来错了,因为Cell
不能保证锁下set()
和锁上get()
之间的同步.
非写Atomic_.store(true, Ordering::Release)
会影响其他原子操作吗?
我试着用AtomicPtr
来写它,看起来很像Java风格,但失败了.我找不到在这种情况下正确使用AtomicPtr
的例子.
非写
Atomic_.store(true, Ordering::Release)
会影响其他原子操作吗?
对
实际上,Ordering
存在的主要原因是对非原子读写施加一些排序保证:
Relaxed
不那么拘束的Ordering
人;唯一不能重新排序的操作是对相同原子值的操作:
atomic.set(4, Ordering::Relaxed);
other = 8;
println!("{}", atomic.get(Ordering::Relaxed));
保证打印4
张.如果另一个线程读取到atomic
是4
,则无法保证other
是否是8
.
Release/Acquire
写入和读取障碍分别为:
store
个操作一起使用,并保证执行之前的写入操作,load
个操作一起使用,并保证进一步读取的值至少与相应store
之前写入的值一样 fresh .所以:
// 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
存储和Acquire
负载或Release
存储和Relaxed
负载基本上没有意义.
请注意,Rust提供了AcqRel
:它的作用相当于Release
用于存储,Acquire
用于装载,所以您不必记住哪个是哪个...不过,我不建议这样做,因为提供的担保是如此不同.
SeqCst
最受限制的Ordering
个.保证一次在所有线程之间排序.
在Rust中写入双重判断锁定的正确方法是什么?
所以,双重判断锁定就是利用这些原子操作来避免不必要的锁定.
这个 idea 是有三件:
并使用它们:
难点在于确保非原子读/写的顺序正确(并且以正确的顺序可见).从理论上讲,你需要有完整的围栏;实际上,遵循C11/C++11内存模型的习惯用法就足够了,因为编译器must使其工作.
让我们先判断一下代码(简化):
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()
}
}
有3个原子操作,通过注释编号.我们现在可以判断每种内存顺序都必须提供哪种保证才能保证正确性.
(1) 如果为true,则返回对该值的引用,该值必须引用有效内存.这要求在原子变为真之前执行对该内存的写入,只有在原子变为真之后才执行对该内存的读取.因此(1)需要Acquire
,而(3)需要Release
.
(2) 另一方面,没有这样的限制,因为锁定Mutex
相当于一个完整的内存屏障:所有写入都保证在之前发生,所有读取只在之后发生.因此,这个负载不需要进一步的保证,所以Relaxed
是最优化的.
因此,就我而言,这种双重判断的实现在实践中看起来是正确的.
为了进一步阅读,我真的推荐the article by Preshing个链接在你链接的文章中.它突出了理论(围栏)和实践(原子负载/存储降低到围栏)之间的差异.