Description 参考资料
如何保证共享数据多线程安全
共享只读数据。
不共享数据。
保护好共享的数据
保证数据被访问的时候是 valid 的
共享只读数据
共享只读数据 就是在多线程中,只能对变量进行读操作,不能进行写操作。
只对数据进行只读操作呢 ?是 Rust 类型系统里面规定了,引用在任何时候只能是下面两种情况之一,而不能同时存在:
有任意个只读引用
有一个可变引用(通过这个引用可以修改引用的变量)
这样就会防止数据在一个线程被写,而在另外的线程被读取或者写。
(concept:: Sync 和 Send, 'static)
Send
不共享数据 就是**不要在多线程中共享变量,
Rust 对此在编译器做了保证,防止不共享的变量被共享了,而这靠的就是Send trait 。
Send 在多线程程序中起到了什么作用呢?满足 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约束的类型,能在多线程之间安全的排它使用 (Exclusive access is thread-safe),所有权类型
满足Send约束的类型T,表示T和&mut T(mut表示能修改这个引用,甚至于删除即drop这个数据)这两种类型的数据能在多个线程之间传递
直白些:能在多个线程之间move值以及修改引用到的值 。
Sync
需要又读又写共享的数据时,Rust 要求共享的被读写的数据满足Sync trait 。
如果数据不满足 Sync trait,但被共享于多线程中,编译器就会报错。
因为只有被保护的数据才会满足 Sync,而被保护了,就说明它可以安全地在多线程间共享 。
比如当用 RefCell 不能用于多线程,主要的信息是RefCell cannot be shared between threads safely
因为 RefCell 里面的数据结构没有被保护,所以不能用于多线程中。需要使用 Mutex 对数据进行保护,才能将数据用于多线程中读和写。所以需要将RefCell<usize>改成Mutex<RefCell<usize>>
类型的实例可以被存储在可以跨线程访问的集合中,例如sd::sync::Arc、std::sync::Mutex、std::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类型,大多是同时满足Send和Sync这两个约束的,因为这些类型的共享引用(&)既能在多个多个线程中使用。
'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
Reactions are currently unavailable
You can’t perform that action at this time.
参考资料
如何保证共享数据多线程安全
共享只读数据
(concept:: Sync 和 Send, 'static)
Send
vector v在主线程创建以后,直接 move 给了生成的线程,那么除了那个线程,没有其他的地方可以使用这个 vector。handle.join().unwrap())前面尝试打印 vector,Rust 就会报错Rc<i32>不能 send 在 threads safelySend 的要求
Send约束的类型,能在多线程之间安全的排它使用(Exclusive access is thread-safe),所有权类型Send约束的类型T,表示T和&mut T(mut表示能修改这个引用,甚至于删除即drop这个数据)这两种类型的数据能在多个线程之间传递move值以及修改引用到的值。Sync
RefCell<usize>改成Mutex<RefCell<usize>>sd::sync::Arc、std::sync::Mutex、std::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类型,大多是同时满足Send和Sync这两个约束的,因为这些类型的共享引用(&)既能在多个多个线程中使用。'static
什么是'static 的生命周期呢?
#type/rust #public