面试题答案
一键面试数组初始化技巧
Vec
动态数组:- 由于需要应对动态变化的数组大小,
Vec
是一个很好的选择。Vec
是 Rust 标准库提供的动态数组类型,它在堆上分配内存,允许在运行时动态增长和收缩。 - 例如,初始化一个存储顶点数据的
Vec
:
let mut vertex_data: Vec<f32> = Vec::new(); for _ in 0..num_vertices { vertex_data.push(x); vertex_data.push(y); vertex_data.push(z); }
- 这种方式虽然简单,但对于大量数据的初始化可能效率不高。可以使用
Vec::with_capacity
预先分配足够的容量,避免多次重新分配内存:
let mut vertex_data: Vec<f32> = Vec::with_capacity(num_vertices * 3); for _ in 0..num_vertices { vertex_data.push(x); vertex_data.push(y); vertex_data.push(z); }
- 由于需要应对动态变化的数组大小,
Box<[T]>
动态切片:- 当知道数组大小在运行时确定,但不想使用
Vec
的一些额外功能(如动态增长)时,可以使用Box<[T]>
。它本质上是一个指向堆上切片的智能指针。 - 例如,初始化一个固定大小的颜色数据数组:
let color_data: Box<[u8; 4 * num_colors]> = Box::new([0; 4 * num_colors]);
- 这里
[0; 4 * num_colors]
创建了一个长度为4 * num_colors
的数组,初始值都为 0,然后通过Box::new
将其分配到堆上。
- 当知道数组大小在运行时确定,但不想使用
vec![T]
宏:- 这是一个方便的宏,用于快速初始化
Vec
。对于一些较小的固定内容数组初始化非常有用。 - 例如,初始化一个表示红色的颜色数组:
let red_color: Vec<u8> = vec![255, 0, 0, 255];
- 这是一个方便的宏,用于快速初始化
线程安全问题解决
Arc
和Mutex
:- 如果需要在多个线程间共享数组数据,可以使用
Arc
(原子引用计数)和Mutex
(互斥锁)。Arc
允许在多个线程间共享数据,Mutex
用于保护数据的访问,确保同一时间只有一个线程可以修改数据。 - 例如,共享顶点数据:
use std::sync::{Arc, Mutex}; let vertex_data = Arc::new(Mutex::new(Vec::new())); let vertex_data_clone = vertex_data.clone(); std::thread::spawn(move || { let mut data = vertex_data_clone.lock().unwrap(); for _ in 0..num_vertices { data.push(x); data.push(y); data.push(z); } });
- 如果需要在多个线程间共享数组数据,可以使用
RwLock
:- 当读操作远多于写操作时,可以使用
RwLock
(读写锁)。它允许多个线程同时进行读操作,但写操作时会独占锁。 - 例如,共享颜色数据,多个线程可能读取颜色数据,偶尔有线程更新它:
use std::sync::{Arc, RwLock}; let color_data = Arc::new(RwLock::new(Vec::new())); let color_data_clone = color_data.clone(); std::thread::spawn(move || { let data = color_data_clone.read().unwrap(); // 进行读操作 }); let color_data_clone2 = color_data.clone(); std::thread::spawn(move || { let mut data = color_data_clone2.write().unwrap(); // 进行写操作 });
- 当读操作远多于写操作时,可以使用
数据一致性问题解决
- 事务性操作:
- 如果数组的更新操作需要保证数据一致性,可以将相关操作包装成一个事务。例如,在更新顶点数据时,如果有多个相关的数组(如位置数组和法线数组)需要同时更新,可以将这些更新操作放在一个锁的保护下,确保要么所有更新都完成,要么都不完成。
let vertex_positions = Arc::new(Mutex::new(Vec::new())); let vertex_normals = Arc::new(Mutex::new(Vec::new())); let vertex_positions_clone = vertex_positions.clone(); let vertex_normals_clone = vertex_normals.clone(); std::thread::spawn(move || { let mut positions = vertex_positions_clone.lock().unwrap(); let mut normals = vertex_normals_clone.lock().unwrap(); // 同时更新 positions 和 normals positions.push(x); positions.push(y); positions.push(z); normals.push(nx); normals.push(ny); normals.push(nz); });
- 版本控制:
- 可以为数组数据引入版本号。每次数据更新时,版本号增加。读取数据的线程可以先读取版本号,然后根据版本号决定是否重新读取数据,以确保读取到最新的一致数据。
struct DataWithVersion<T> { data: T, version: u32, } let vertex_data = Arc::new(Mutex::new(DataWithVersion { data: Vec::new(), version: 0, })); let vertex_data_clone = vertex_data.clone(); std::thread::spawn(move || { let mut data = vertex_data_clone.lock().unwrap(); data.data.push(x); data.data.push(y); data.data.push(z); data.version += 1; }); let vertex_data_clone2 = vertex_data.clone(); std::thread::spawn(move || { let data = vertex_data_clone2.lock().unwrap(); let current_version = data.version; // 根据 current_version 决定是否重新读取 data.data });