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

异步等待已运行任务:全局资源懒加载避免重复数据库调用的实现咨询

解决全局资源懒加载的并发重复调用问题

你遇到的这个场景很典型——并发触发懒加载逻辑时,重复执行了数据库查询,浪费了资源。你的思路方向完全正确:通过缓存初始化任务,让后续请求等待同一个正在运行的任务,而不是重复执行初始化逻辑。下面是适配async/await模式的正确实现,完全不会有手动操作Task的冲突问题:

核心思路

我们用一个Task类型的字段来缓存初始化任务,替代单纯的_isLoaded标记:

  1. 第一个请求进入时,创建并启动初始化任务,将其存入字段
  2. 后续请求进来时,直接等待这个已存在的任务完成
  3. 利用async方法的天然特性,不需要手动调用Task.Start()——async方法返回的Task本身就是"热任务",调用方法时内部逻辑就已经开始执行了

修改后的GlobalResources实现

public class GlobalResources {
    // 缓存初始化任务,null表示未开始加载
    private Task<string>? _loadTask;
    private readonly object _loadLock = new object();
    private string _externalQueryResult = string.Empty;

    public async Task LoadAsync() {
        // 快速检查,避免不必要的锁竞争
        if (_loadTask != null) {
            await _loadTask;
            return;
        }

        lock (_loadLock) {
            // 双重检查,确保只有第一个请求会创建初始化任务
            if (_loadTask != null) {
                await _loadTask;
                return;
            }

            // 仅第一次进入时执行,创建并启动数据库查询任务
            _loadTask = LongRunningQuery();
        }

        // 等待初始化任务完成
        await _loadTask;
        // 将查询结果赋值到字段(如果需要后续使用)
        _externalQueryResult = await _loadTask;
    }

    public async Task<string> LongRunningQuery() {
        Console.WriteLine("Loading data from Database ( this line should only be written once on console window).");
        await Task.Delay(5000);
        return "123";
    }
}

关键细节解释

  1. 双重检查锁的正确用法:

    • 外层的快速检查避免了每次请求都进入锁,提升并发场景下的性能
    • 内层锁里的二次检查,彻底防止多个并发请求同时创建初始化任务
  2. async任务的特性适配:

    • 调用LongRunningQuery()时,方法内部的代码会立即开始执行,返回的Task代表这个正在运行的操作
    • 后续请求await _loadTask时,会自动等待同一个任务完成,不会触发新的数据库调用
  3. 可选的异常处理:
    如果你的初始化逻辑可能失败,且希望后续请求可以重试,可以在任务完成后判断状态,若失败则重置任务:

    try {
        await _loadTask;
        _externalQueryResult = await _loadTask;
    } catch {
        // 加载失败,重置任务让下一次请求可以重试
        lock (_loadLock) {
            _loadTask = null;
        }
        throw; // 将异常抛出给调用方处理
    }
    

测试验证

运行你原有的测试代码,现在控制台只会输出一次Loading data from Database...,两个MyToolClass的InitAsync都会等待同一个任务完成,完全符合你的需求。

内容的提问来源于stack exchange,提问作者Alois

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 04:42:58