rust - 如果通过原子操作进行门控,非原子写入是否可以安全读取?

标签 rust atomic

我想创建一个“空”但可以处理复杂数据(这里是ab)的对象,以后可以更新该对象并设置一个atomic标志将其标记为非空,以便可以在其他线程中使用。伪示例:

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上看到它

看来可行,但这并不意味着什么。我最担心的是,对ab的写入是否确实对其他将is_empty读取为false的线程可见。如果我保证:
  • 所有对ab的写操作都在设置标志
  • 之前发生
  • 在设置标志
  • 之前,没有线程读取ab
    这个可以吗?

    我可以改用AtomicPtr,完全创建对象,并交换指针,但是我很好奇是否可以避免额外的间接调用。

    最佳答案

    您可能想使用Release and Acquire而不是SeqCst

    Release :

    When coupled with a store, all previous operations become ordered before any load of this value with Acquire (or stronger) ordering. In particular, all previous writes become visible to all threads that perform an Acquire (or stronger) load of this value.

    Acquire :

    When coupled with a load, if the loaded value was written by a store operation with Release (or stronger) ordering, then all subsequent operations become ordered after that store. In particular, all subsequent loads will see data written before the store.



    更改此:

    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();
    }
    

    另请参见docsnomicon

    关于rust - 如果通过原子操作进行门控,非原子写入是否可以安全读取?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60331404/

    相关文章:

    c++ - 为什么 memory_order 作为 std::atomic 函数的运行时参数给出

    linux - Linux 上 Solaris Studio 中的原子操作

    c++ - 有很多原子是好是坏?

    rust - 我如何告诉 Rust 某些东西实现了一个特征?

    rust - 如何获取函数的地址?

    rust - Substrate:如何与 session 模块集成

    c - 原子加载和存储函数生成与非原子加载和存储相同的汇编代码

    C++ 原子数组是否也需要是原子的?

    rust - 如何将整个 crate 传递给程序宏?

    rust - 如何最普遍地迭代具有给定类型的一系列项目?