Rust多线程 - 线程间共享数据

线程间共享数据的方式

  • 使用 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 {
// 添加微小延迟增加竞争可能性
// thread::sleep(Duration::from_nanos(1));
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());
}