返回首页

Rust 中并行状态的 Arc 和 Mutex

本文介绍了在多线程 Rust 应用程序中使用 Arc 和 Mutex 安全访问共享状态。包括计数器示例、智能指针比较和优化建议。适合中高级开发者。

Rust 中的并行状态:Arc + Mutex 实际应用
Advertisement 728x90

Rust多线程应用中的安全共享状态访问

线程间未受保护的内存共享会导致竞态条件,即并发读写破坏数据。即使使用同步机制,也可能发生死锁,拖慢执行速度。调试此类代码颇具挑战性,不过Rust能在编译时阻止许多错误。

在高负载系统(如计数器或缓存)中,使用适当的同步原语时,共享访问是合理的。

使用Arc和Mutex实现线程安全的数据共享

为了在线程间安全传输数据,需结合使用Arc<T>Mutex<T>Arc<T>提供原子引用计数,支持SendSync,允许多线程环境中的多重所有权。Mutex<T>保证互斥:一次仅有一个线程访问数据。

Google AdInline article slot

使用Arc::clone为每个线程克隆Arc,并通过lock()获取访问权限,该方法返回MutexGuardlock()方法可能在互斥锁中毒时引发恐慌,因此在生产环境中应处理错误,而非使用unwrap

计数器实现示例

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

struct Counter {
    value: i64
}

fn main() {
    let counter = Arc::new(Mutex::new(Counter {value: 0}));

    let increment = Arc::clone(&counter);
    let handler1 = thread::spawn(move || {
        for _ in 0..10 {
            let mut guard = increment.lock().unwrap();
            guard.value += 1;
            println!("inc: {}", guard.value);
            thread::sleep(Duration::from_millis(100));
        }
    });

    let reader = Arc::clone(&counter);
    let handler2 = thread::spawn(move || {
        for _ in 0..15 {
            let guard = reader.lock().unwrap();
            println!("read: {}", guard.value);
            thread::sleep(Duration::from_millis(180));
        }
    });

    handler1.join().unwrap();
    handler2.join().unwrap();

    let final_guard = counter.lock().unwrap();
    println!("final: {}", final_guard.value);
}

此代码演示了一个线程递增、另一个线程读取的过程。最终值始终为10,无数据竞争。

关键智能指针对比

Rust提供多种智能指针用于内存管理。选择取决于线程安全性和所有权需求:

Google AdInline article slot
  • Box<T>:堆上的单一所有权,适用于大小未知的类型,非Send + Sync
  • Rc<T>:单线程内的多重所有权,非线程安全。
  • Arc<T>:跨线程的多重所有权,Send + Sync,原子操作带来开销。
  • RefCell<T>:运行时借用检查的内部可变性,单线程。
  • Cell<T>Copy类型的轻量级可变性,无恐慌。
  • Mutex<T>:独占访问的锁,有死锁风险。
  • RwLock<T>:读多写少场景的优化。
  • Weak<T>:弱引用,用于打破Rc/Arc循环。
  • ManuallyDrop<T>:手动析构器控制,不安全。

Arc<T>是标准库中唯一支持线程安全多重所有权的智能指针。

用于优化的Mutex替代方案

在频繁读取的场景中,考虑使用RwLock<T>:多个读取器可并行进行,写入器独占访问。当读取器/写入器比率大于1时,性能更高。

对于无锁访问,如果数据是原始类型(如AtomicI64),可使用原子操作(std::sync::atomic)。它们完全避免锁。

Google AdInline article slot

关键要点

  • 共享访问仅在高负载系统且有共享数据时合理;否则,优先考虑所有权转移。
  • 始终结合使用Arc<T>Mutex<T>RwLock<T>以确保线程安全。
  • 避免在lock()上使用unwrap():处理PoisonError
  • Arc<T>因原子操作带来开销——在基准测试中衡量。
  • 对于简单情况,使用通道(mpsc)而非共享状态。

— Editorial Team

Advertisement 728x90

继续阅读