示例代码
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<AppState>, user: web::Json<Auth>) -> 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<AppState>) -> Result<bool, Box<dyn Error + Send + Sync>> {
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<AppState>) -> Result<(), Box<dyn Error + Send + Sync>> {
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(())
}线程阻塞原因分析
异步函数中嵌套使用同步锁导致的死锁:
// 在verify_password函数中
let conn = data.db.conn.lock().unwrap(); // 获取数据库连接锁
// ...
if is_first_login(data.clone()).await.unwrap() { // 调用异步函数
update_login_status(data.clone()).await.unwrap(); // 又调用异步函数
}关键问题点:
- 主线程在
verify_password中获取了数据库连接的同步锁(std::sync::Mutex) - 然后调用了异步函数
is_first_login和update_login_status - 这两个异步函数内部又尝试获取同一个数据库连接锁
- 主线程持有锁并等待异步函数完成,而异步函数又等待主线程释放锁
- 形成了经典的死锁场景
解决思路
1. 在调用异步函数前释放锁
pub async fn verify_password(data: web::Data<AppState>, user: web::Json<Auth>) -> 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. 使用异步锁替代同步锁
// 在数据库模型中使用tokio的异步锁
use tokio::sync::Mutex;
// 然后在处理函数中使用await获取锁
let conn = data.db.conn.lock().await;3. 重构为一次性数据库操作
pub async fn verify_password(data: web::Data<AppState>, user: web::Json<Auth>) -> 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. 使用数据库连接池
// 在AppState中使用连接池而非单个连接
use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
// 每个请求获取独立连接,避免锁竞争
let conn = data.db_pool.get().unwrap();5. 使用非阻塞I/O和futures-aware的数据库驱动
// 使用支持异步的数据库驱动,如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