侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

使用多线程安全修改共享计数器

2025-12-14 / 0 评论 / 2 阅读

题目

使用多线程安全修改共享计数器

信息

  • 类型:问答
  • 难度:⭐

考点

线程创建,共享数据同步,Arc与Mutex使用

快速回答

修正代码的关键步骤:

  1. 使用Arc<Mutex<T>>包装共享数据
  2. 在闭包内克隆Arc并移动所有权
  3. 通过lock()获取互斥锁修改数据

正确代码结构:

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

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..2 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            // 加锁修改数据
        });
        handles.push(handle);
    }
    // 等待线程结束
}
## 解析

问题背景

在Rust并发编程中,当多个线程需要修改同一共享数据时,必须解决两个核心问题:1) 所有权的跨线程传递;2) 数据竞争的避免。原始代码尝试直接在线程闭包中移动Mutex的所有权,这违反了Rust的所有权规则。

错误代码分析

// 错误示例
let counter = Mutex::new(0);
thread::spawn(move || { 
    // 错误:counter被第一个线程移动后,第二个线程无法使用
    let mut num = counter.lock().unwrap();
    *num += 1;
});

主要错误:

  • 所有权冲突:move闭包尝试获取counter的所有权,但多个线程不能共享同一所有权
  • 未实现共享:Mutex本身不具备多线程共享能力,需要配合智能指针

解决方案

使用Arc<Mutex<T>>组合:

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

fn main() {
    // 用Arc包装Mutex
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..2 {
        // 克隆Arc指针(增加引用计数)
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            for _ in 0..1000 {
                // 获取互斥锁
                let mut num = counter.lock().unwrap();
                *num += 1;  // 安全修改数据
            } // 锁自动释放
        });
        handles.push(handle);
    }

    // 等待所有线程完成
    for handle in handles {
        handle.join().unwrap();
    }

    // 打印最终结果
    println!("Result: {}", *counter.lock().unwrap());
}

核心原理

  • Arc(原子引用计数):实现所有权的多线程共享,通过克隆增加引用计数
  • Mutex(互斥锁):确保同时只有一个线程能访问数据,防止数据竞争
    • lock()方法阻塞线程直到获取锁
    • 返回的MutexGuard在离开作用域时自动释放锁

最佳实践

  1. 最小化锁范围:在{}代码块内持有锁,尽快释放
  2. 错误处理:使用lock().expect("锁错误")处理可能的PoisonError
  3. 性能考虑:对于高并发场景,可评估RwLock(读写锁)替代

常见错误

错误类型现象解决方案
忘记克隆Arc编译错误:所有权已移动每次循环内克隆Arc
未处理锁错误线程panic导致锁污染使用unwrapexpect
锁嵌套死锁风险避免在持有一个锁时获取另一个锁

扩展知识

  • 原子类型:对于简单整数操作,AtomicUsizeMutex更高效
  • 作用域线程std::thread::scope可避免显式使用Arc
  • 无锁编程:高阶主题,使用crossbeam等库实现复杂并发结构