You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:22:07