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

如何在多线程环境下实现u128类型的原子加法操作?

关于多线程环境下u128计数器的安全实现问题

背景

在多线程环境中运行程序,需要每秒25-50次将64位值添加到全局u128类型计数器中。

初始实现的漏洞分析

你给出的初始AtomicU128实现存在严重线程安全问题:
当多个线程同时触发lsv溢出时,每个线程都会独立执行msv.fetch_add(1),导致msv被重复累加。比如lsv初始为u64::MAX,两个线程同时调用fetch_add(1),每个线程都会判断出溢出并给msv加1,但实际上只需要加1就能正确表示128位溢出结果,最终msv会变成2而非1,导致计数器值完全错误。
此外,该实现没有保证lsv和msv的更新是原子操作,线程间的操作可能交叉,导致整体状态不一致。

用Mutex包裹时是否需要AtomicU64?

不需要。
Mutex的核心作用是保证同一时间只有一个线程能访问被包裹的数据,此时内部使用普通u128就足够安全。原子类型(AtomicU64)是用于无锁线程同步场景的,在已经用Mutex做互斥的情况下,原子类型的特性完全用不上,反而会带来不必要的开销。

更优的u128安全加法实现方式

根据你的操作频率(每秒25-50次,属于低频率),以下几种方案均可选择:

方案1:简化Mutex封装

你现有的Mutex实现已经足够安全,还可以进一步简化,甚至无需自定义结构体,直接使用Arc<Mutex<u128>>:

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(u64::MAX as u128));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter_clone = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut inner = counter_clone.lock().unwrap();
            *inner += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final value: {}", *counter.lock().unwrap());
}

该方案实现简单,完全基于标准库,对于低频率操作来说性能完全够用。

方案2:无锁CAS实现AtomicU128

如果想要无锁实现(虽然对你的频率来说必要性不大),可以通过CAS循环保证128位加法的原子性:

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;

#[derive(Debug)]
pub struct AtomicU128 {
    low: AtomicU64,
    high: AtomicU64,
}

impl AtomicU128 {
    pub fn new(value: u128) -> Self {
        AtomicU128 {
            low: AtomicU64::new((value & u64::MAX as u128) as u64),
            high: AtomicU64::new((value >> 64) as u64),
        }
    }

    pub fn fetch_add(&self, value: u64) -> u128 {
        loop {
            // 读取当前高低位的一致快照
            let current_high = self.high.load(Ordering::SeqCst);
            let current_low = self.low.load(Ordering::SeqCst);
            let current = (current_high as u128) << 64 | current_low as u128;

            // 计算新值
            let new = current + value as u128;
            let new_low = (new & u64::MAX as u128) as u64;
            let new_high = (new >> 64) as u64;

            // CAS更新低位,失败则重试
            if self.low.compare_exchange_weak(
                current_low,
                new_low,
                Ordering::SeqCst,
                Ordering::Relaxed,
            ).is_ok() {
                // 高位有变化时再CAS更新
                if current_high != new_high {
                    self.high.compare_exchange(
                        current_high,
                        new_high,
                        Ordering::SeqCst,
                        Ordering::SeqCst,
                    ).unwrap();
                }
                return current;
            }
        }
    }

    pub fn load(&self) -> u128 {
        let high = self.high.load(Ordering::SeqCst);
        let low = self.low.load(Ordering::SeqCst);
        (high as u128) << 64 | low as u128
    }
}

fn main() {
    let counter = Arc::new(AtomicU128::new(u64::MAX as u128));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter_clone = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            counter_clone.fetch_add(1);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final value: {}", counter.load());
    println!("{:?}", counter);
}

该实现通过CAS循环保证了128位加法的原子性,避免了Mutex的开销,但实现相对复杂。对于你的低频率操作,Mutex方案已经完全够用。

总结

  • 你的操作频率极低,Mutex方案是最简单且足够安全的选择,无需过度追求无锁实现。
  • 若确实需要无锁,上述CAS循环实现可保证线程安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 09:34:53