侧边栏壁纸
博主头像
colo

欲买桂花同载酒

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

使用 Mutex 实现线程安全的计数器

2025-12-12 / 0 评论 / 4 阅读

题目

使用 Mutex 实现线程安全的计数器

信息

  • 类型:问答
  • 难度:⭐

考点

线程创建, 共享数据保护, Mutex 基础使用

快速回答

实现线程安全计数器的核心步骤:

  • 使用 Arc<Mutex<T>> 包装共享数据
  • 通过 lock().unwrap() 获取互斥锁
  • 在锁作用域内修改数据
  • 使用 thread::spawn 创建线程

最终确保多个线程能安全地递增计数器。

解析

问题场景

在并发编程中,当多个线程需要修改同一份数据时,必须通过同步机制防止数据竞争。本题要求创建两个线程,各将共享计数器递增10次,最终结果应为20。

核心原理

Rust 通过所有权系统和类型系统保证并发安全:

  • Mutex(互斥锁):确保同一时间只有一个线程能访问数据
  • Arc(原子引用计数):允许在多线程间安全共享所有权
  • 获取锁后返回 MutexGuard,实现:
    • 自动释放锁(Drop trait)
    • 提供数据的可变引用

代码实现

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 || {
            for _ in 0..10 {
                // 获取锁并修改数据
                let mut num = counter.lock().unwrap();
                *num += 1;
            } // 锁在此自动释放
        });
        handles.push(handle);
    }

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

    // 验证结果
    println!("Final count: {}", *counter.lock().unwrap());
}

最佳实践

  • 锁作用域最小化:在 {} 内完成操作后立即释放锁
  • 错误处理:生产代码中应处理 lock()Result 而非直接 unwrap()
  • 避免死锁:不在持锁时调用可能阻塞的函数

常见错误

  • 直接使用 Mutex 不加 ArcMutex 本身不实现 Clone,无法跨线程共享
  • 忘记获取锁:直接修改数据会导致编译错误(所有权检查)
  • 长期持锁:在循环内持锁执行耗时操作会降低并发性能

扩展知识

  • RwLock:读写分离锁,适用于读多写少场景
  • 原子类型AtomicUsize 等适用于简单操作,无需显式锁
  • 死锁预防:按固定顺序获取多个锁,或使用 try_lock