如何在C#循环中异步调用无返回值方法(Async/Await)
批量异步加密数据库用户密码的实现方案
问题背景
现有EncryptAllExistingTrackerUsersPasswordsAsync方法用于批量加密数据库中存储的明文密码,当前逻辑是逐个获取用户、计算哈希、同步更新数据库。需求改为让循环内的操作异步并行执行,无需等待前一次操作完成。
现有代码问题
原PutEncryptedPasswordBackToDatabase方法内部调用了异步的ExecuteNonQueryAsync但未使用await,属于"火并"操作,会导致异步任务未被正确等待,可能引发资源泄漏或操作未完成的问题。
改造步骤及代码实现
1. 修正数据库更新方法为异步
首先将数据库更新方法改为真正的异步方法,确保异步操作被正确等待:
public async Task PutEncryptedPasswordBackToDatabaseAsync(int id, string hash, byte[] salt) { var sql = "UPDATE Tracker_Users SET [HashedPassword] = @HashedPassword, [Salt] = @Salt WHERE ID = @TrackerUserId"; await _dbManager.ExecuteNonQueryAsync( sql, id.AsParam("TrackerUserId"), hash.AsParam("HashedPassword"), salt.AsParam("Salt", System.Data.SqlDbType.VarBinary) ); }
2. 实现并行异步加密逻辑
修改主方法,使用Task.WhenAll结合信号量控制并发,避免数据库过载:
private async Task EncryptAllExistingTrackerUsersPasswordsAsync() { var trackerUsers = await _userContext.GetAllTrackerUsers(); // 限制并发数,根据数据库承载能力调整,示例设为10 using var semaphore = new SemaphoreSlim(10); var tasks = new List<Task>(); foreach (var user in trackerUsers) { await semaphore.WaitAsync(); tasks.Add(Task.Run(async () => { try { HashSalt hashsalt = _hash.EncryptPassword(user.Password); await _userContext.PutEncryptedPasswordBackToDatabaseAsync(user.Id, hashsalt.Hash, hashsalt.Salt); } finally { semaphore.Release(); } })); } // 等待所有异步任务完成 await Task.WhenAll(tasks); }
关键说明
- 并发控制:使用
SemaphoreSlim限制同时执行的异步任务数量,防止短时间内大量数据库请求耗尽连接池或压垮数据库。 - 异步正确性:所有异步方法都添加
await,确保操作真正异步执行且能捕获异常。 - 线程安全:需确保
_hash.EncryptPassword方法是线程安全的,若为CPU密集型操作,可根据实际情况调整并行策略。 - 风险规避:操作前建议备份数据库,可添加日志记录每个用户的加密状态,方便排查异常。
内容的提问来源于stack exchange,提问作者Hassaan Khawaja
相关产品推荐
相关产品推荐

