线程间共享数据的方式
- 使用 move 转移所有权
- 使用限定作用域的线程(Scoped Threads)从生命周期更长的父线程借用数据
- Static
- Box::leak()
- Arc
Static
- Static 变量的值在整个程序运行期间都有效
- 拥有 ‘static 生命周期
- 只能用常量值初始化
- 代表了一个内存地址,可以进行引用
- 在程序结束时不会调用 drop
- 可以是 mut 的,或非 mut 的
实操
非 mut static :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use std::thread;
static DATA: [i32; 5] = [1, 2, 3, 4, 5];
fn main() { let mut handles = Vec::new(); for _ in 0..100 { let handle = thread::spawn(|| { println!("Data: {DATA:?}"); }); handles.push(handle); }
handles.into_iter().for_each(|handle| { handle.join().unwrap(); }) }
|
mut static : 线程不安全的,最后结果可能不是10000,如果多次是可以放开注释掉的 sleep
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| use std::thread;
static mut COUNTER: u32 = 0;
fn main() { let mut handles = Vec::new(); for _ in 0..10000 { let handle = thread::spawn(|| unsafe { COUNTER += 1; }); handles.push(handle); }
handles .into_iter() .for_each(|handle| handle.join().unwrap());
println!("COUNTER: {}", unsafe { COUNTER }); }
|
Box::leak()
本质:主动泄露内存分配
- 释放 Box 的所有权,并承诺永远不会drop它
- 从 leak 这一刻起,这个 Box 就一直存在了
- 缺点是:它是内存泄漏,一个程序中不要用太多
实操
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use std::thread;
fn main() { let data: &'static [i32; 5] = Box::leak::<'static>(Box::new([1, 2, 3, 4, 5])); let mut handles = Vec::new();
for _ in 0..10000 { let handle = thread::spawn(move || { println!("Data: {data:?}"); }); handles.push(handle); }
handles .into_iter() .for_each(|handle| handle.join().unwrap()); }
|
Arc 原子引用计数(atomically reference counted)
- 与 Rc 类似,但 Arc 保证对引用计数器的修改是不可分割的原子操作
- 可以用在多线程环境中
实操
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| use std::{sync::Arc, thread};
fn main() { let data = Arc::new([1, 2, 3, 4, 5]); let mut handles = Vec::new();
for _ in 0..10000 { let local_data = data.clone(); let handle = thread::spawn(move || { println!("Data: {local_data:?}"); }); handles.push(handle); }
handles .into_iter() .for_each(|handle| handle.join().unwrap()); }
|