线程安全

本节更多探讨RUST 关于线程安全的设计

线程安全

线程安全,指多个线程再竞争访问同一个数据内容时,不会出现数据损坏从而引发意外行为

RUST只对数据安全执行了保护,并不会防止死锁问题

#![feature(mutex_unlock)]


use std::thread;
use std::sync::Arc;
use std::sync::Mutex;

fn main() {

    let a = Arc::new(Mutex::new(10));

    let c = a.lock().unwrap();// a is locked
    let a1 = a.clone();
    let child = thread::spawn( move || {
        println!("{}",a1.lock().unwrap()) ;//try to lock a, will failed ,unless a release lock
    });


    Mutex::unlock(c);// call    drop(c); 
    child.join().expect("Failed join child thread");
    println!("main exit");
}

那么RUST是如何保证 数据的共享和正确访问呢?

线程创建的时候,会约束闭包使用变量的类型

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,

可以看到,F 和 T 类型都被约束为 必须要支持Send 特征;如果闭包中使用了不支持Send特征的类型,编译不会通过

Send 特征

通过Send特征标记 一个数据类型是否可以被 发送(移动) 到线程中 表明该类型是一个移动类型

支持Send特征的类型,因为所有权再发送给线程之后 可以转移,因此是安全的

pub unsafe auto trait Send { }

可以看到该特征是一个 auto 类型,因此类型默认都会默认实现该特征,同时 unsafe 表明了该特征是不安全特征 需要开发人员明确自定义类型是支持线程同步访问的

RC被明确的声明为 不支持Send 特征,因此Rc指针不可以被发送给线程

impl<T: ?Sized, A: Allocator> !Send for Rc<T, A> {}

指针也是不支持Send特征的类型

use std::thread;

const A:i32 = 10;
fn main() {

    let a = 10;
    let b = &a as *const i32;
    let child = thread::spawn( move || {
        println!("{}", *b) ;
    });
    child.join().expect("Failed join child thread");
    println!("main exit");
}

引用也无法传递(借用声明周期约束)

#![feature(negative_impls)]

use std::thread;

#[derive(Debug)]
struct Apple;

impl !Sync for Apple {}

fn main() {

    let a = Apple;
    let b = &a;
    let child = thread::spawn( move || {
        println!("{:?}", *b) ;
    });
    child.join().expect("Failed join child thread");
    println!("main exit");
}

Sync 特征

Sync特征的存在,主要是解决数据共享类型的,Sync标识的是所有权转移 而Sync标识的是同一个内存可以再不同 的线程访问

  • 如果类型支持Sync 标记,那么该类型的 不可变引用 支持Send标记
  • 如果类型支持Send,那么该类型的可变引用 也支持send
  • 如果类型支持Sync 那么该类型的引用和不可变引用 支持Send
// 不可变引用 &String,String 是 Sync,因此 &String 是 Send

fn main() {
    let s = String::from("Hello");
    let reference = &s;

    std::thread::spawn(move || {
        println!("{}", reference);
    }).join().unwrap();
}


// 可变引用 &mut Vec<i32>,Vec<i32> 是 Send,因此 &mut Vec<i32> 是 Send
fn main() {
    let mut vec = vec![1, 2, 3];
    let reference = &mut vec;

    std::thread::spawn(move || {
        reference.push(4);
        println!("{:?}", reference);
    }).join().unwrap();
}

#[derive(Debug)]
struct Apple;

const DATA :Apple = Apple;

fn main() {
    let refd:&'static Apple = &DATA;
    std::thread::spawn(move || {
        println!("captured {:?} by value", *refd)
    }).join().unwrap();

    println!("{:?}",DATA);
}