如何在C#中构建多请求共享慢数据的async/await代码结构
解决方案:用
Lazy<Task<T>>实现异步结果的共享缓存 在.NET中,你可以用Lazy<Task<T>>完美实现这个场景——它能保证仅执行一次异步数据库调用,所有后续请求都会等待第一个请求完成,之后直接复用缓存的结果。
核心思路
Lazy<T>本身提供线程安全的延迟初始化,结合Task<T>就能适配异步场景的结果共享:
- 第一个触发请求的UI组件会启动数据库调用任务;
- 后续组件请求时,直接等待已启动的任务完成,不会发起新请求;
- 任务完成后,所有后续请求直接获取缓存的结果。
代码实现
1. 定义数据模型(示例)
public class AppData { public int RecordId { get; set; } public string DisplayName { get; set; } // 根据业务需求添加其他字段 }
2. 实现缓存服务类
这个类封装数据库调用和缓存逻辑,建议作为单例使用(比如静态实例或依赖注入):
public class DataCache { private Lazy<Task<AppData>> _cachedTask; public DataCache() { // 初始化Lazy,传入异步数据库查询逻辑 _cachedTask = new Lazy<Task<AppData>>(FetchDataFromDbAsync, LazyThreadSafetyMode.ExecutionAndPublication); } // 对外提供的获取数据方法 public Task<AppData> GetDataAsync() { return _cachedTask.Value; } // 实际的数据库调用逻辑 private async Task<AppData> FetchDataFromDbAsync() { // 替换为你的数据库连接字符串和查询语句 using var connection = new SqlConnection("YourDatabaseConnectionString"); await connection.OpenAsync(); const string query = "SELECT RecordId, DisplayName FROM YourTargetTable"; using var command = new SqlCommand(query, connection); using var reader = await command.ExecuteReaderAsync(); if (await reader.ReadAsync()) { return new AppData { RecordId = reader.GetInt32(0), DisplayName = reader.GetString(1) }; } return null; // 或根据业务需求抛出异常 } // 可选:如果后续需要重置缓存(比如数据变更,虽然你提到数据不会变) public void ResetCache() { _cachedTask = new Lazy<Task<AppData>>(FetchDataFromDbAsync, LazyThreadSafetyMode.ExecutionAndPublication); } }
3. 在Windows Forms组件中使用
各个UI组件可以独立调用缓存服务,无需互相感知:
// 全局实例化缓存服务(或通过依赖注入) private readonly DataCache _dataCache = new DataCache(); // 示例:按钮点击事件中获取数据 private async void btnGetData_Click(object sender, EventArgs e) { btnGetData.Enabled = false; try { var data = await _dataCache.GetDataAsync(); // 更新UI控件 lblData.Text = $"ID: {data.RecordId}, Name: {data.DisplayName}"; } catch (Exception ex) { MessageBox.Show($"加载失败: {ex.Message}"); } finally { btnGetData.Enabled = true; } }
方案优势
- 线程安全:
LazyThreadSafetyMode.ExecutionAndPublication确保只有第一个访问_cachedTask.Value的线程会执行数据库调用,其他线程等待任务完成; - 异步友好:全程使用
async/await,不会阻塞UI线程,符合Windows Forms的异步编程规范; - 轻量无依赖:无需额外NuGet包,直接使用.NET基础类库实现。
如果使用.NET 6+,也可以用Microsoft.VisualStudio.Threading包中的AsyncLazy<T>,它是Lazy<Task<T>>的封装,用法类似,但Lazy<Task<T>>更轻量。
内容的提问来源于stack exchange,提问作者Ann L.
相关产品推荐
相关产品推荐

