面试题答案
一键面试1. 并行矩阵转置示例
use std::thread;
fn parallel_transpose(matrix: &mut Vec<Vec<i32>>) {
let num_threads = num_cpus::get();
let num_rows = matrix.len();
let num_cols = matrix[0].len();
let row_chunks: Vec<&mut Vec<i32>> = matrix.chunks_mut(num_rows / num_threads).collect();
let mut handles = vec![];
for chunk in row_chunks {
let handle = thread::spawn(move || {
for i in 0..chunk.len() {
for j in (i + 1)..num_cols {
let temp = chunk[i][j];
chunk[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
2. 安全地共享和修改数据及处理竞争条件
- 共享数据:在上述代码中,我们通过
move
语义将矩阵的一部分所有权转移到闭包中,使得每个线程都处理矩阵的不同部分,避免了数据竞争。如果需要共享不可变数据,可以使用&
引用。例如,如果矩阵不需要修改,我们可以将闭包参数改为&Vec<Vec<i32>>
。 - 修改数据:当需要修改共享数据时,为了避免竞争条件,我们可以使用
Mutex
或RwLock
。例如:
use std::sync::{Mutex, Arc};
let shared_matrix = Arc::new(Mutex::new(matrix));
let mut handles = vec![];
for _ in 0..num_threads {
let matrix_clone = shared_matrix.clone();
let handle = thread::spawn(move || {
let mut matrix = matrix_clone.lock().unwrap();
// 对矩阵进行修改操作
});
handles.push(handle);
}
这里Arc
用于在多个线程间共享Mutex
,Mutex
则保证同一时间只有一个线程能访问和修改矩阵。
3. 闭包在并行场景中的生命周期管理和所有权转移
- 生命周期管理:在
thread::spawn
中使用闭包时,闭包捕获的变量的生命周期需要满足线程执行的时间。例如,上述代码中move
闭包将chunk
的所有权转移到闭包中,chunk
的生命周期至少要持续到线程结束。如果闭包捕获的是引用,那么引用的生命周期也需要覆盖线程执行的时间。 - 所有权转移:
move
关键字使得闭包获取捕获变量的所有权。在上述并行矩阵转置的例子中,每个闭包通过move
获取chunk
的所有权,这意味着在闭包执行期间,外部作用域不再拥有chunk
的所有权。如果闭包捕获的变量实现了Send
trait(表示该类型可以安全地在线程间传递),就可以在不同线程间转移所有权。