You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用AtomicCell::compare_exchange更新结构体指定字段?

问题描述
  • 需求场景:一个线程每10ms更新f64类型的角度数据,另一个线程每1秒仅在数据有更新时打印该角度值。
  • 技术选型:使用crossbeam crate的AtomicCell<Struct>实现线程间数据共享。
  • 现存问题:
    • 需要原子性执行两个操作:仅当结构体的is_updated字段为true时,将其重置为false,同时获取对应的角度值。
    • 当前代码通过两次独立原子操作实现,存在竞态条件,不具备完全原子性。
    • 因存在无需关注is_updated状态、直接读取角度值的需求,无法使用AtomicF64 crate。

原始尝试代码

use crossbeam::atomic::AtomicCell;

#[derive(Default, Copy, Clone)]
struct AnglePosition {
    is_updated: bool,
    angle: f64,
}

fn main() {
    let angle_pos = AtomicCell::new(AnglePosition {
        is_updated: true,
        angle: 56.789,
    });
    
    // 期望用AtomicCell::compare_exchange实现:仅当is_updated为true时将其设为false,同时保留angle值
    // 以下操作非完全原子,因为是两次独立原子操作
    let updated_angle_pos = angle_pos.load();
    if updated_angle_pos.is_updated {
        angle_pos.store(AnglePosition {
            is_updated: false,
            angle: updated_angle_pos.angle,
        });
        println!("angle position data updated");
    }
    
    let updated_angle_pos = angle_pos.load();
    if !updated_angle_pos.is_updated {
        println!("angle position data not updated after last read");
    }
}
解决方案

要实现完全原子的状态检查与更新,需利用AtomicCell::compare_exchange的原子特性:基于当前加载的结构体实例构造预期值和目标值,通过单次原子操作完成匹配检查与值替换。

核心实现思路

  1. 加载当前的AnglePosition实例作为预期匹配值。
  2. 若预期值的is_updated为true,构造新值:保留原角度值,将is_updated设为false。
  3. 调用compare_exchange:如果当前内存中的值与预期值完全匹配,则原子替换为新值;若不匹配(其他线程已修改数据),则重新加载当前值重试。

完整可运行代码

use crossbeam::atomic::AtomicCell;
use std::thread;
use std::time::Duration;

#[derive(Default, Copy, Clone, PartialEq)]
struct AnglePosition {
    is_updated: bool,
    angle: f64,
}

fn main() {
    let angle_pos = AtomicCell::new(AnglePosition::default());

    // 角度更新线程:每10ms更新角度并标记为已更新
    thread::spawn(move || {
        let mut angle = 0.0;
        loop {
            angle_pos.store(AnglePosition {
                is_updated: true,
                angle,
            });
            angle += 1.0;
            thread::sleep(Duration::from_millis(10));
        }
    });

    // 打印线程:每1秒检查并打印更新后的角度
    thread::spawn(move || {
        loop {
            loop {
                let current = angle_pos.load();
                if !current.is_updated {
                    break;
                }
                // 构造目标值:保留原角度,重置更新标记
                let new_val = AnglePosition {
                    is_updated: false,
                    angle: current.angle,
                };
                // 原子交换操作:仅当当前值与预期值一致时执行替换
                match angle_pos.compare_exchange(current, new_val) {
                    Ok(_) => {
                        println!("更新后的角度: {}", current.angle);
                        break;
                    }
                    Err(_) => {
                        // 其他线程修改了数据,重新加载重试
                        continue;
                    }
                }
            }
            thread::sleep(Duration::from_secs(1));
        }
    });

    // 保持主线程存活
    loop {
        thread::sleep(Duration::from_secs(10));
    }
}

关键细节说明

  • 原子性保障:compare_exchange是单一硬件原子操作,彻底避免了先加载再存储的竞态条件,确保"检查更新标记+重置标记"的操作不可分割。
  • 重试逻辑:当compare_exchange返回Err时,说明其他线程在加载值和尝试交换的间隙修改了数据,此时重新加载当前值并重试,确保不会遗漏任何更新。
  • 直接读取支持:依然可以通过angle_pos.load().angle直接获取最新角度值,完全满足无需关注is_updated状态的读取需求。

内容的提问来源于stack exchange,提问作者Harry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 16:13:28