rust操作数据库同步锁,调用异步函数,触发死锁问题 最后更新时间:2025年10月27日 ## 示例代码 ```rust use actix_web::{web, Responder}; use crate::handlers::AppState; use crate::models::auth::Auth; use crate::utils::password::password_utils; use crate::utils::api_response::ApiResponse; use std::error::Error; pub async fn verify_password(data: web::Data, user: web::Json) -> impl Responder { let mut password = None; let conn = data.db.conn.lock().unwrap(); // 查询用户 let mut stmt = conn.prepare("SELECT password FROM sys_user WHERE username = ? and enable = 0").unwrap(); let rows = stmt.query_map(&[&user.username], |row| row.get::<_, String>(0)).unwrap(); for row in rows { password = Some(row.unwrap()); break; } // 验证密码 if let Some(pwd) = password { match password_utils::verify_password(&user.password, &pwd) { Ok(true) => { if is_first_login(data.clone()).await.unwrap() { update_login_status(data.clone()).await.unwrap(); } ApiResponse::success(Some("Password verified successfully".to_string())) }, Ok(false) => ApiResponse::error(None,None,Some("Incorrect password".to_string())), Err(_) => ApiResponse::error(None,None,Some("Password verification error".to_string())), } } else { ApiResponse::error(None,None,Some("User not found".to_string())) } } // 判断系统是否是第一次登录 pub async fn is_first_login(data: web::Data) -> Result> { let conn = data.db.conn.lock().unwrap(); println!("{:?}",conn); // 查询用户 let mut stmt = conn.prepare("select value from sys_config where key = 'first_login' and value = '0' limit 0,1").unwrap(); let rows = stmt.query_map([], |row| row.get::<_, String>(0)).unwrap(); let mut value = None; for row in rows{ value = Some(row.unwrap()); break; } println!("{:?}",value); // 判断是否第一次登录 if value == Some("0".to_string()) { Ok(true) } else { Ok(false) } } // 更新登录状态 pub async fn update_login_status(data: web::Data) -> Result<(), Box> { let conn: std::sync::MutexGuard<'_, rusqlite::Connection> = data.db.conn.lock().unwrap(); // 更新登录状态 let mut stmt = conn.prepare("update sys_config set value = '1' where key = 'first_login'").unwrap(); stmt.execute([]).unwrap(); Ok(()) } ``` ## 线程阻塞原因分析 **异步函数中嵌套使用同步锁导致的死锁**: ```rust // 在verify_password函数中 let conn = data.db.conn.lock().unwrap(); // 获取数据库连接锁 // ... if is_first_login(data.clone()).await.unwrap() { // 调用异步函数 update_login_status(data.clone()).await.unwrap(); // 又调用异步函数 } ``` **关键问题点:** 1. 主线程在`verify_password`中获取了数据库连接的同步锁(`std::sync::Mutex`) 2. 然后调用了异步函数`is_first_login`和`update_login_status` 3. 这两个异步函数内部又尝试获取同一个数据库连接锁 4. 主线程持有锁并等待异步函数完成,而异步函数又等待主线程释放锁 5. 形成了经典的死锁场景 ## 解决思路 ### 1. 在调用异步函数前释放锁 ```rust pub async fn verify_password(data: web::Data, user: web::Json) -> impl Responder { let mut password = None; { // 开始一个新的作用域 let conn = data.db.conn.lock().unwrap(); // 查询用户 let mut stmt = conn.prepare("SELECT password FROM sys_user WHERE username = ? and enable = 0").unwrap(); let rows = stmt.query_map(&[&user.username], |row| row.get::<_, String>(0)).unwrap(); for row in rows { password = Some(row.unwrap()); break; } } // 作用域结束,锁释放 // 验证密码 if let Some(pwd) = password { match password_utils::verify_password(&user.password, &pwd) { Ok(true) => { if is_first_login(data.clone()).await.unwrap() { update_login_status(data.clone()).await.unwrap(); } ApiResponse::success(Some("Password verified successfully".to_string())) }, Ok(false) => ApiResponse::error(None,None,Some("Incorrect password".to_string())), Err(_) => ApiResponse::error(None,None,Some("Password verification error".to_string())), } } else { ApiResponse::error(None,None,Some("User not found".to_string())) } } ``` ### 2. 使用异步锁替代同步锁 ```rust // 在数据库模型中使用tokio的异步锁 use tokio::sync::Mutex; // 然后在处理函数中使用await获取锁 let conn = data.db.conn.lock().await; ``` ### 3. 重构为一次性数据库操作 ```rust pub async fn verify_password(data: web::Data, user: web::Json) -> impl Responder { let conn = data.db.conn.lock().unwrap(); // 验证密码逻辑... // 合并查询和更新操作,避免多次获取锁 let is_first = conn.prepare("select value from sys_config where key = 'first_login' and value = '0'") .unwrap() .query_map([], |row| row.get::<_, String>(0)) .unwrap() .next() .map_or(false, |r| r.unwrap() == "0"); if is_first { conn.execute( "update sys_config set value = '1' where key = 'first_login'", [] ).unwrap(); } ApiResponse::success(Some("Password verified successfully".to_string())) } ``` ### 4. 使用数据库连接池 ```rust // 在AppState中使用连接池而非单个连接 use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; // 每个请求获取独立连接,避免锁竞争 let conn = data.db_pool.get().unwrap(); ``` ### 5. 使用非阻塞I/O和futures-aware的数据库驱动 ```rust // 使用支持异步的数据库驱动,如sqlx use sqlx::sqlite::SqlitePool; // 异步查询和更新 let is_first = sqlx::query_scalar("select value from sys_config where key = 'first_login' and value = '0'") .fetch_optional(&data.db_pool) .await?; ```
Comments | NOTHING