• rust的Sync和Send对比


    std::marker::Sync

    可以安全地在线程之间共享引用的类型(it is safe to share references between threads)。
    编译器认为合适时(compiler determines it’s appropriate),此trait会自动实现。

    精确的定义(precise definition ):

    impl<T> Send for &T
    where
        T: Sync + ?Sized,
    
    • 1
    • 2
    • 3

    换句话说,如果在线程之间传递 &T 引用时,不能存在任何未定义行为(包括数据争用)

    像 u8 和 f64 这样的基本类型(primitive types)都是 Sync 的,包含它们的简单聚合类型也是如此(so are simple aggregate types containing them),比如元组、结构体和枚举。
    基本Sync类型的更多示例包括“immutable”类型,例如 &T,以及具有简单继承可变性的类型(those with simple inherited mutability),例如 Box、Vec 和大多数其他集合类型。
    (通用参数需要Sync,才能使其容器Sync)
    (Generic parameters need to be Sync for their container to be Sync.)

    该定义的一个有点令人惊讶的结果(somewhat surprising consequence)是 &mut T is Sync if T is Sync,尽管看起来可能会提供unsynchronized的突变(provide unsynchronized mutation)。
    技巧在于共享引用(shared reference)后面的可变引用(mutable reference)(即 & &mut T)变为只读,as if it were a & &T(就像它是 & &T 一样)。因此不存在数据争用的风险。

    因为 Send trait 要求类型 T 是线程安全的,而 Sync trait 要求引用 &T 在多线程环境中是安全的。

    Sync 和 Send如何与引用相关(how Sync and Send relate to referencing)的简短概述:

    • &T is Send if and only if T is Sync
    • &mut T is Send if and only if T is Send
    • &T and &mut T are Sync if and only if T is Sync
    use std::sync::{Mutex, Arc};
    use std::thread;
    
    fn main() {
        // 创建一个互斥锁(Mutex)
        // Creates a new mutex in an unlocked state ready for use
        // 类型:alloc::sync::Arc>
        let mutex = Arc::new(Mutex::new(0));
    
        // 克隆互斥锁的 Arc 指针,以便可以在多个线程中共享
        let mutex1 = Arc::clone(&mutex);
        let mutex2 = Arc::clone(&mutex);
    
        // 创建一个新的线程,修改互斥锁中的值
        let thread1 = thread::spawn(move || {
            let mut data = mutex1.lock().unwrap();
            *data += 1;
        });
    
        // 创建另一个新的线程,修改互斥锁中的值
        let thread2 = thread::spawn(move || {
            let mut data = mutex2.lock().unwrap();
            *data += 2;
        });
    
        // 等待线程结束
        thread1.join().unwrap();
        thread2.join().unwrap();
    
        // 打印最终互斥锁的值
        let data = mutex.lock().unwrap();
        println!("Final value: {}", *data);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    impl<T> Sync for Arc<T>
    where
        T: Sync + Send + ?Sized,
    
    impl<T: ?Sized + Send> Send for Mutex<T>
    impl<T: ?Sized + Send> Sync for Mutex<T>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    以上文为例

    impl Send for i32
    
    • 1

    所以
    Mutex 具有 Send、Sync
    Arc 具有 Sync

    non-Sync 类型是那些以非线程安全形式具有“内部可变性”的类型(have “interior mutability” in a non-thread-safe form),例如 Cell 和 RefCell。即使通过不可变的共享引用(an immutable, shared reference),这些类型也允许更改其内容(allow for mutation of their contents)。例如,Cell 上的 set 方法采用 &self,因此它只需要共享引用 &Cell。该方法不进行同步(performs no synchronization),因此Cell不是Sync的

    non-Sync 类型的另一个示例是引用计数指针(reference-counting pointer) Rc。给定任何引用 &Rc,您可以克隆一个新的 Rc,以非原子方式修改引用计数(modify the reference counts in a non-atomic way)。

    对于确实需要线程安全的内部可变性的情况(thread-safe interior mutability),Rust 提供了原子数据类型(atomic data types),以及通过sync::Mutex 和sync::RwLock 进行显式锁(explicit locking)。这些类型确保任何突变都不会导致数据争用(any mutation cannot cause data races),因此这些类型是Sync的。同样,sync::Arc 提供了 Rc 的线程安全类似物(a thread-safe analogue of Rc)。

    任何具有内部可变性的类型(Any types with interior mutability)还必须使用 cell::UnsafeCell wrapper来包装可以通过共享引用进行变异的值(can be mutated through a shared reference)。不这样做是未定义的行为。例如,从 &T 转换为 &mut T 是无效的。

    有关同步的更多详细信息,请参阅 https://doc.rust-lang.org/nomicon/send-and-sync.html

    std::marker::Send

    可以跨线程传输的类型(Types that can be transferred across thread boundaries)。
    当编译器认为合适时,此特征会自动实现。

    non-Send的一个示例是引用计数指针 rc::Rc(reference-counting pointer)。如果两个线程尝试克隆指向相同引用计数值的 Rc,它们可能会尝试同时更新引用计数,这是未定义的行为,因为 Rc 不使用原子操作。它的表兄弟(cousin)sync::Arc使用原子操作(产生一些开销,incur some overhead),因此是Send的。

    区别

    Sync trait用于标记类型是线程安全的,可以安全地在多个线程中共享
    Send trait用于标记类型可以安全地在线程之间转移所有权

    如果一个类型实现了Sync trait,那么它就可以被多个线程同时拥有的&T引用所访问,其中T是该类型的实例
    如果一个类型实现了Send trait,那么它可以安全地在不同线程之间转移所有权。

    附录

    Arc的Clone

    impl<T> Clone for Arc<T>
    where
        T: ?Sized,
    
    fn clone(&self) -> Arc<T>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    克隆 Arc 指针(Makes a clone of the Arc pointer)。
    这会创建另一个指向同一分配(same allocation)的指针,从而增加强引用计数(strong reference count)

    use std::sync::Arc;
    let five = Arc::new(5);
    let _ = Arc::clone(&five);
    
    • 1
    • 2
    • 3
  • 相关阅读:
    处理Centos 7 中buff/cache高的问题
    吃鸡达人必备:分享顶级干货+作图工具推荐+账号安全查询!
    【JavaSE】继承与多态(下篇)
    利用ipv6把电脑做成服务器(ssl443端口)
    意向客户的信息获取到底是怎样的,快来get一下
    CDGA考试-2022年最新模拟题一套100道题(含答案)
    Java#19(面向对象三大特征之一:多态)
    好细的Vue安装与配置
    Excel、Jira、Bugfree 应该选哪个做bug管理?深度对比
    Linux CentOS7安装MySQL(yum方式)
  • 原文地址:https://blog.csdn.net/wangkai6666/article/details/133336310