rust - 如果由原子操作门控,非原子写入是否可以安全读取?
问题描述
我想创建一个“空”但可以稍后更新的复杂数据(此处a
和b
)的对象,并设置一个原子标志以将其标记为非空,以便它可以在其他线程中使用。伪示例:
use std::sync::atomic::{AtomicBool, Ordering};
use std::cell::Cell;
use std::sync::Arc;
use std::{thread, time};
struct MyObject {
is_empty: AtomicBool,
a: Cell<u64>,
b: Cell<u64>,
}
unsafe impl Sync for MyObject {}
fn main() {
let obj = Arc::new(MyObject {
is_empty: AtomicBool::new(true),
a: Cell::new(0),
b: Cell::new(0)
});
let thread_obj = obj.clone();
let t = thread::spawn(move || {
while thread_obj.is_empty.load(Ordering::SeqCst) {
thread::sleep(time::Duration::from_millis(10));
}
println!("a is: {}", thread_obj.a.get());
println!("b is: {}", thread_obj.b.get());
});
thread::sleep(time::Duration::from_millis(100));
obj.a.set(42);
obj.b.set(5);
obj.is_empty.store(false, Ordering::SeqCst);
t.join().unwrap();
}
在Rust Playground上看到它
它似乎有效,但这并不意味着什么。我最关心的是,如果写入a
并且肯定会被其他读为假b
的线程看到。is_empty
如果我保证:
- 所有写入
a
并b
在设置标志之前发生 - 在设置标志之前
a
没有线程读取b
这个可以吗?
我可以使用 anAtomicPtr
代替,完全创建对象,然后交换指针,但我很好奇是否可以避免额外的间接。
解决方案
您可能想使用Release 和 Acquire而不是SeqCst
释放 :
当与存储结合使用时,所有先前的操作都会在使用 Acquire(或更强)排序的任何加载该值之前排序。特别是,所有先前的写入对执行该值的获取(或更强)加载的所有线程都是可见的。
获得 :
当与加载相结合时,如果加载的值是由具有发布(或更强)排序的存储操作写入的,则所有后续操作都在该存储之后排序。特别是,所有后续加载都将看到在存储之前写入的数据。
改变这个:
fn main() {
let obj = Arc::new(MyObject {
is_empty: AtomicBool::new(true),
a: Cell::new(0),
b: Cell::new(0)
});
let thread_obj = obj.clone();
let t = thread::spawn(move || {
while thread_obj.is_empty.load(Ordering::SeqCst) {
thread::sleep(time::Duration::from_millis(10));
}
println!("a is: {}", thread_obj.a.get());
println!("b is: {}", thread_obj.b.get());
});
thread::sleep(time::Duration::from_millis(100));
obj.a.set(42);
obj.b.set(5);
obj.is_empty.store(false, Ordering::SeqCst);
t.join().unwrap();
}
进入 :
fn main() {
let obj = Arc::new(MyObject {
is_empty: AtomicBool::new(true),
a: Cell::new(0),
b: Cell::new(0)
});
let thread_obj = obj.clone();
let t = thread::spawn(move || {
while thread_obj.is_empty.load(Ordering::Acquire){ // change
thread::sleep(time::Duration::from_millis(10));
}
println!("a is: {}", thread_obj.a.get());
println!("b is: {}", thread_obj.b.get());
});
thread::sleep(time::Duration::from_millis(100));
obj.a.set(42);
obj.b.set(5);
obj.is_empty.store(false, Ordering::Release); //change
t.join().unwrap();
}
推荐阅读
- excel - 在搜索中找不到目标值时如何结束关闭宏
- intellij-idea - 为 Groovy 脚本(XmlParser 或 XmlSplurper)导入库到 Intellij Idea
- c++ - 为什么 for 循环不增加?
- awk - awk - get least count where key appears minimum time
- node.js - 从 ng start 启用 v8 profiler 的启动节点
- html - 如何为 Chrome 编写 @font-face
- git - 为什么在 git checkout 上它会自动在我的新分支中添加更改的文件
- javascript - 围绕 z 轴旋转对象,取决于一个点
- angular - 如何为 Validators.required 创建自定义验证
- java - 为什么使用jmstemplate时报告的activemq队列数会发生变化?