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

如何在Rust中实现并发近似计数器?解决Arc可变借用报错

解决Rust近似计数器的Arc可变借用错误

我正在阅读《操作系统:三个简单部分》(OS: the three easy pieces)第29章的并发数据结构内容,第一个示例是近似计数器。该数据结构通过全局Mutex和多个带本地计数器的本地Mutex实现数值递增,当本地计数器达到阈值时,会获取全局Mutex并将本地计数刷新到全局计数器。

我参考章节提供的C语言实现尝试用Rust改写,但遇到了Deref trait相关错误,提示无法对Arc<ApproximateCounter>进行可变借用。以下是相关代码和错误信息,求修改方案。

C语言原实现

typedef struct __counter_t {
    int global; // global count
    pthread_mutex_t glock; // global lock
    int local[NUMCPUS]; // per-CPU count
    pthread_mutex_t llock[NUMCPUS]; // ... and locks
    int threshold; // update frequency
} counter_t;

// init: record threshold, init locks, init values
// of all local counts and global count
void init(counter_t *c, int threshold) {
    c->threshold = threshold;
    c->global = 0;
    pthread_mutex_init(&c->glock, NULL);
    int i;

    for (i = 0; i < NUMCPUS; i++) {
        c->local[i] = 0;
        pthread_mutex_init(&c->llock[i], NULL);
    }
}


// update: usually, just grab local lock and update
// local amount; once local count has risen ’threshold’,
// grab global lock and transfer local values to it
void update(counter_t *c, int threadID, int amt) {
    int cpu = threadID % NUMCPUS;
    pthread_mutex_lock(&c->llock[cpu]);
    c->local[cpu] += amt;
    if (c->local[cpu] >= c->threshold) {
        // transfer to global (assumes amt>0)
        pthread_mutex_lock(&c->glock);
        c->global += c->local[cpu];
        pthread_mutex_unlock(&c->glock);
        c->local[cpu] = 0;
    }
    pthread_mutex_unlock(&c->llock[cpu]);
}

// get: just return global amount (approximate)
int get(counter_t *c) {
    pthread_mutex_lock(&c->glock);
    int val = c->global;
    pthread_mutex_unlock(&c->glock);
    return val; // only approximate!
}

我的Rust实现(存在错误)

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


pub struct Counter {
    value: Mutex<i32>
}

impl Counter {
    pub fn new() -> Self {
        Counter { value: Mutex::new(0)}
    }

    pub fn test_and_increment(&mut self) -> i32 {
        let mut value = self.value.lock().unwrap();
        *value += 1;

        if *value >= 10 {
            let old = *value;
            *value = 0;
            return old;
        }
        else {
            return 0;
        }
    }

    pub fn get(&mut self) -> i32 {
        *(self.value.lock().unwrap())
    }

    pub fn add(&mut self, value: i32) {
        *(self.value.lock().unwrap()) += value;
    }
}


impl fmt::Display for Counter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", *self.value.lock().unwrap())
    }
}


pub struct ApproximateCounter {
    value: Counter,
    local_counters: [Counter; 4]
}

impl ApproximateCounter {
    pub fn new() -> Self {
        ApproximateCounter {
            value: Counter::new(),
            local_counters: [Counter::new(), Counter::new(), Counter::new(), Counter::new()]
        }
    }

    pub fn increment(&mut self, i: usize) {
        let local_value = self.local_counters[i].test_and_increment();

        if local_value > 0 {
            self.value.add(local_value);
        }
    }

    pub fn get(&mut self) -> i32 {
        self.value.get()
    }
}

fn main() {
    let mut counter = Arc::new(ApproximateCounter::new());
    let mut threads = Vec::new();
    for i in 0..4 {
        let c_counter = counter.clone();
        threads.push(thread::spawn(move || {
            for _ in 0..100 {
                c_counter.increment(i);
            }
        }));
    }
    for thread in threads {
        thread.join();
    }
    println!("{}", counter.get());
}

错误信息

error[E0596]: cannot borrow data in an `Arc` as mutable
  --> src/main.rs:54:21
   |
54 |                     c_counter.increment(i);
   |                     ^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<ApproximateCounter>

问题原因与修改方案

核心问题

Arc本身只提供共享不可变访问,不支持直接可变借用(因为Arc没有实现DerefMut)。你的代码里increment和get方法都要求&mut self,但多线程场景下Arc克隆后无法提供可变引用。

另外,你的Counter结构体已经用Mutex内部封装了可变性,外部不需要再用&mut self——Mutex的锁机制已经保证了内部数据的线程安全可变访问。

具体修改点

  • 移除所有方法的&mut self,改为&self:因为Mutex负责内部可变性,外部只需要共享引用即可。
  • 修正main中的变量声明:不需要mut修饰counter,因为Arc的克隆是共享不可变的,而内部的可变性由Mutex处理。
  • 导入thread模块:原代码里漏掉了use std::thread;,会导致编译错误。

修正后的Rust代码

use std::fmt;
use std::sync::{Arc, Mutex};
use std::thread; // 新增导入thread模块

pub struct Counter {
    value: Mutex<i32>
}

impl Counter {
    pub fn new() -> Self {
        Counter { value: Mutex::new(0) }
    }

    // 改为&self
    pub fn test_and_increment(&self) -> i32 {
        let mut value = self.value.lock().unwrap();
        *value += 1;

        if *value >= 10 {
            let old = *value;
            *value = 0;
            old
        } else {
            0
        }
    }

    // 改为&self
    pub fn get(&self) -> i32 {
        *self.value.lock().unwrap()
    }

    // 改为&self
    pub fn add(&self, value: i32) {
        *self.value.lock().unwrap() += value;
    }
}

impl fmt::Display for Counter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", *self.value.lock().unwrap())
    }
}

pub struct ApproximateCounter {
    value: Counter,
    local_counters: [Counter; 4]
}

impl ApproximateCounter {
    pub fn new() -> Self {
        ApproximateCounter {
            value: Counter::new(),
            local_counters: [Counter::new(), Counter::new(), Counter::new(), Counter::new()]
        }
    }

    // 改为&self
    pub fn increment(&self, i: usize) {
        let local_value = self.local_counters[i].test_and_increment();

        if local_value > 0 {
            self.value.add(local_value);
        }
    }

    // 改为&self
    pub fn get(&self) -> i32 {
        self.value.get()
    }
}

fn main() {
    // 移除mut修饰
    let counter = Arc::new(ApproximateCounter::new());
    let mut threads = Vec::new();
    for i in 0..4 {
        let c_counter = counter.clone();
        threads.push(thread::spawn(move || {
            for _ in 0..100 {
                c_counter.increment(i);
            }
        }));
    }
    for thread in threads {
        thread.join().unwrap(); // 处理join的Result,避免警告
    }
    println!("{}", counter.get());
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 05:40:37