Rust多线程下共享变量


阅读须知

阅读本文,你可以知道:

  • 使用Arc,在多线程访问共享不可变变量
  • Arc与Mutex、Arc与Atomic,在多线程访问共享可变变量

但是,本文不涉及原理性介绍,请自行搜索。

Arc

线程安全的引用计数器,Arc代表Atomaticlly Reference Counted/ 原子引用计数。

该类型Arc提供T在堆中分配的type值的共享所有权。调用clone将Arc产生一个新Arc实例,该实例指向堆上与源相同的分配Arc,同时增加引用计数。当Arc 指向给定分配的最后一个指针被破坏时,存储在该分配中的值(通常称为“内部值”)也会被删除。

默认情况下,Rust中的共享引用不允许更改,Arc也不例外:您通常无法获得对内某些内容的可变引用Arc。如果通过需要发生变异Arc,使用MutexRwLock或者一个Atomic类型。

多线程访问共享不可变变量

use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

#[test]
pub fn test_immutable() {
    let b = Arc::new(1);

    let b1 = b.clone();
    let b2 = b.clone();
    let t1 = thread::spawn(move || {
        println!("b1,addr:{:p},values:{}", b1.as_ref(), b1);
    });

    let t2 = thread::spawn(move || {
        println!("b2,addr:{:p},values:{}", b2.as_ref(), b2);
    });

    println!("b,addr:{:p},values:{}", b.as_ref(), b);

    t2.join();
    t1.join();
}

Mutex

互斥器(mutex) 是mutual exlusion的缩写,也就是说,任意时刻,其只允许一个线程访问某些数据。为了访问互斥器中的数据,线程首先需要获得互斥器的,来表明其希望访问数据。

Arc+Mutex实现多线程下多所有权

use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

#[test]
pub fn test_mutable() {
    let b = Arc::new(Mutex::new(1));

    let mut handles = vec![];

    // 10个线程对b加1
    for i in 10..20 {
        let b1 = Arc::clone(&b);
        let handle = thread::Builder::new().name(format!("thread-{}", i)).spawn(move || {
            // lock 调用返回MutexGuard的智能指针,它实现了Deref来指向其内部数据,其也提供了一个Drop实现,当它离开作用域时自动释放锁
            let mut guard = b1.lock().unwrap();
            *guard = *guard + 1;
            println!("{},addr:{:p},value{}", thread::current().name().unwrap(),b1.as_ref() ,*guard);
            // guard 离开作用域后会自动释放锁
        }).unwrap();
        handles.push(handle);
    }
    // 10个线程对b减1
    for i in 0..10 {
        let b2 = Arc::clone(&b);
        let handle = thread::Builder::new().name(format!("thread-{}", i)).spawn(move || {
            let mut guard = b2.lock().unwrap();
            *guard = *guard - 1;
            println!("{},addr:{:p},value{}", thread::current().name().unwrap(),b2.as_ref() ,*guard);
        }).unwrap();
        handles.push(handle);
    }

    // for handle in handles{
    //     handle.join().unwrap();
    // }
    thread::sleep(Duration::from_secs(1));
    println!("b,addr:{:p},values:{}", b.as_ref(), b.lock().unwrap());
}

AtomicUsize

Arc+AtomicUsize 实现多线程下多所有权

use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

#[test]
fn test_mutable() {
    let arc = Arc::new(AtomicUsize::new(1));
    let mut handles = vec![];
    for _ in 0..10 {
        let val = Arc::clone(&arc);
        let handle = thread::spawn(move || {
            let v = val.fetch_add(1, Ordering::SeqCst);
            println!("{:?},{:p}", v, Arc::::as_ptr(&val));
        });
        handles.push(handle);
    }

    for _ in 0..10 {
        let val = Arc::clone(&arc);
        let handle = thread::spawn(move || {
            let v = val.fetch_sub(1, Ordering::SeqCst);
            println!("{:?},{:p}", v, Arc::::as_ptr(&val));
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("final,{:?},{:p}", arc, Arc::::as_ptr(&arc));
}

参考链接

https://kaisery.github.io/trpl-zh-cn/ch16-00-concurrency.html