如何在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
相关产品推荐
相关产品推荐

