MST

星途 面试题库

面试题:Rust中集成外部crate处理并发任务及避免死锁

在一个多线程Rust项目中,要使用外部crate来处理并发任务,例如使用`tokio` crate 。要求你设计一个场景,涉及多个线程竞争资源,阐述如何合理使用`tokio`相关功能来避免死锁,并给出关键代码示例及对代码逻辑的解释。
15.1万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

设计场景

假设有一个银行转账的场景,多个线程可能同时尝试从一个账户向另一个账户转账。每个账户有一个余额,转账操作需要先锁定转出账户,再锁定转入账户,以确保余额的一致性。如果不妥善处理,可能会发生死锁,例如线程A锁定了账户1并等待账户2,而线程B锁定了账户2并等待账户1。

使用Tokio避免死锁

Tokio提供了tokio::sync::Mutex来处理并发访问。为避免死锁,可以使用tokio::sync::MutexGuard的自动释放特性,以及按照固定顺序锁定资源。

关键代码示例

use tokio::sync::Mutex;

// 定义账户结构体
struct Account {
    balance: i32,
}

// 创建账户
let account1 = Mutex::new(Account { balance: 100 });
let account2 = Mutex::new(Account { balance: 200 });

// 转账函数
async fn transfer(
    from: &Mutex<Account>,
    to: &Mutex<Account>,
    amount: i32,
) -> Result<(), String> {
    // 按照相同顺序锁定账户
    let mut from_guard = from.lock().await;
    let mut to_guard = to.lock().await;

    if from_guard.balance < amount {
        return Err("Insufficient balance".to_string());
    }

    from_guard.balance -= amount;
    to_guard.balance += amount;

    Ok(())
}

代码逻辑解释

  1. 账户定义Account结构体包含一个balance字段,用于表示账户余额。
  2. 创建账户:使用Mutex包裹账户,确保并发安全。
  3. 转账函数
    • transfer函数接受两个Mutex引用和转账金额。
    • 使用from.lock().awaitto.lock().await按照固定顺序锁定账户,避免死锁。lock方法返回一个MutexGuard,它会在作用域结束时自动释放锁。
    • 检查转出账户余额是否足够,如果不足则返回错误。
    • 完成转账操作,更新两个账户的余额。

通过这种方式,在多线程环境下使用Tokio的Mutex可以有效避免死锁。