Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rust中的sync和sen #80

Open
BruceChen7 opened this issue Jan 17, 2024 · 0 comments
Open

rust中的sync和sen #80

BruceChen7 opened this issue Jan 17, 2024 · 0 comments

Comments

@BruceChen7
Copy link
Owner

BruceChen7 commented Jan 17, 2024

参考资料

如何保证共享数据多线程安全

  • 共享只读数据。
  • 不共享数据。
  • 保护好共享的数据
  • 保证数据被访问的时候是 valid 的

共享只读数据

  • 共享只读数据就是在多线程中,只能对变量进行读操作,不能进行写操作。
  • 只对数据进行只读操作呢?是 Rust 类型系统里面规定了,引用在任何时候只能是下面两种情况之一,而不能同时存在:
    • 有任意个只读引用
    • 有一个可变引用(通过这个引用能修改引用的变量)
  • 这样就会防止数据在一个线程被写,而在另外的线程被读取或者写。

(concept:: Sync 和 Send, 'static)

Send

  • 不共享数据就是不要在多线程中共享变量
  • 在编译器做了保证,防止不共享的变量被共享了,而这靠的就是Send trait
  • 满足 Send,说明这个变量能安全的在线程间转移
  • 实现了 Send trait 表示是所有权类型
  • 不共享数据的时候,能直接将变量 move 给生成的线程,这样,数据就被这个线程独有了
    let v = vec![1, 2, 3];
    
    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });
    
    handle.join().unwrap();
  • vector v 在主线程创建以后,直接 move 给了生成的线程,那么除了那个线程,没有其他的地方能使用这个 vector。
  • 如果其他地方使用这个 vector(比如,在handle.join().unwrap() )前面尝试打印 vector,Rust 就会报错
  • 数据要在线程之间被 move 需要满足 Send trait
  • 如果move 的变量不满足 Send,那么 Rust 将禁止程序编译通过。
    • Rc<usize>,如果 move 它,在多线程编程中就会报错
    • 报错的重要一句话是 Rc<i32> 不能 send 在 threads safely
    • 上面报错的 Rc。因为Rc 不是多线程安全的引用计数,对应 Rc 并且线程安全的引用计数是 Arc。

Send 的要求

  • 满足Send约束的类型,能在多线程之间安全的排它使用,所有权类型
  • 满足Send约束的类型T,表示T&mut Tmut表示能修改这个引用,甚至于删除即drop这个数据)这两种类型的数据能在多个线程之间传递
  • 能在多个线程之间move值以及修改引用到的值

Sync

  • 需要又读又写共享的数据,要求共享的被读写的数据满足Sync trait
  • 如果数据不满足 Sync trait,但被共享于多线程中,编译器就会报错。
  • 因为只有被保护的数据才会满足 Sync,而被保护了,就说明它能安全地在多线程间共享
  • 比如当用 RefCell 不能用于多线程,主要的信息是RefCell cannot be shared between threads safely
  • 因为 RefCell 里面的数据结构没有被保护,所以不能用于多线程中。
  • 需要使用 Mutex 对数据进行保护,才能将数据用于多线程中读和写。
  • 所以需要将RefCell<usize>改成Mutex<RefCell<usize>>
  • 类型的实例能被存储在能跨线程访问的集合中,例如sd::sync::Arcstd::sync::Mutexstd::sync::RwLock

Sync 和 Send 的关系

  • Sync 能理解为是 Send 的辅助

  • 一个类型实现了Sync trait,不一定就实现了Send trait

  • 如果一个类型实现了Send trait,那么它一定也实现了Sync trait

  • Sync:

  • 满足Sync约束的类型,能在多线程之间安全的共享使用(Shared access is thread-safe)。

  • 满足Sync约束的类型T,只表示该类型能在多个线程中读共享,即:不能move,也不能修改,仅仅只能通过引用&T来读取这个值。

  • 一个类型&T 的只有在满足Send约束的条件下,类型T 才能满足Sync约束 (a type T is Sync if and only if &T is Send)。即:T: Sync ≡ &T: Send

  • 这是因为将数据的引用发送到另一个线程并不会实际移动数据,而只是共享对数据的指针

  • 因此,如果安全地将数据的引用发送到另一个线程,则该数据本身也能同时从多个线程访问

  • 对于那些基本的类型(primitive types)而言,比如i32类型,大多是同时满足SendSync这两个约束的,因为这些类型的共享引用(&)既能在多个多个线程中使用。

(concept:: 'static )

  • Send 和 Sync,规定了多线程中,只能使用线程安全的数据
  • 在 Rust 里面每一个数据都具有 owner,当 owner 失效的时候,数据就被释放/析构。
  • 如果数据被用于多线程中,那么线程要么单独 own 这个数据,也就是前文所说的数据被 move 到线程中;
  • 要么数据具有'static 的生命周期

什么是'static 的生命周期呢?

  • 'static 的生命周期表示 这个变量需要存活多久就能存活多久。满足这个条件的有三种情况:
  • 这个数据存活得跟包含它的程序一样长,也就是数据在程序退出的时候才会被释放/析构。
    • 比如static 的变量,它们在程序被 load 的时候存在,在程序被 unload 的时候释放;
    • 比如 literal string,它们保存在程序的二进制代码中
  • 这个变量是 owned type。
    • 什么是 owned type 呢?,就是这个变量是完全占有 (own) 这个数据,也就是所有权类型
    • 比如 String,Vector,还有 primitive type, usize, i64 等等。
    • 所以编写多线程程序的时候,能选择使用 owned type 将变量 move 到线程里面(这就是第一点不共享数据);
    • 当数据是share onwership 的时候就使用引用计数,结合 Send 和 Sync 来进行多线程编程
  • 'static 生命周期的第三种情况是如果变量包含引用,那么它只包含其他'static 生命周期的引用
    • 显然,虽然这种情况,变量不拥有对应的数据,但是引用是'static,那么它也能存活得想要多长就多长。

#type/rust #public

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant