面试题答案
一键面试Rust互斥锁在异步编程中的挑战
- 阻塞问题:Rust的标准库中的
Mutex
是为同步编程设计的。在异步环境下,如果一个异步任务获取了Mutex
,并且在持有锁期间执行了阻塞操作(例如同步I/O),这会导致整个异步运行时线程被阻塞,影响其他异步任务的执行。 - 死锁风险增加:在异步编程中,多个任务可能并发执行且获取锁的顺序难以预测。如果不同任务以不同顺序获取多个互斥锁,就容易产生死锁。例如,任务A获取锁1,然后尝试获取锁2;而任务B获取锁2,然后尝试获取锁1,此时就可能发生死锁。
在异步场景下有效使用互斥锁保护共享资源
为了在异步场景下有效使用互斥锁,我们可以使用tokio::sync::Mutex
,它是Tokio异步运行时提供的异步互斥锁。
代码示例
use tokio::sync::Mutex;
use std::sync::Arc;
#[tokio::main]
async fn main() {
// 创建一个共享资源,用Arc包裹以实现多线程安全的引用计数
let shared_data = Arc::new(Mutex::new(0));
// 克隆Arc,以便在不同的任务中使用
let shared_data_clone = Arc::clone(&shared_data);
// 创建第一个异步任务
let task1 = tokio::spawn(async move {
let mut data = shared_data_clone.lock().await;
*data += 1;
println!("Task 1 incremented data to: {}", *data);
});
// 创建第二个异步任务
let task2 = tokio::spawn(async move {
let mut data = shared_data.lock().await;
*data += 2;
println!("Task 2 incremented data to: {}", *data);
});
// 等待两个任务完成
task1.await.unwrap();
task2.await.unwrap();
}
在上述代码中:
- 我们使用
tokio::sync::Mutex
创建了一个异步互斥锁来保护shared_data
。 Arc
用于在不同任务间共享这个互斥锁,因为Arc
提供了线程安全的引用计数。- 在每个任务中,通过
lock().await
异步地获取锁。这不会阻塞其他异步任务,因为lock
方法返回一个Future
,需要等待其完成才能获取锁。 - 任务1和任务2分别对共享数据进行操作,确保在同一时间只有一个任务能访问和修改共享数据,从而保护了共享资源。