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

如何异步初始化多线程共享的ISomething资源且避免重复构造?

用AsyncLazy完美解决你的异步共享初始化问题

绝对推荐用AsyncLazy来搞定这个场景——这简直是为你的需求量身定做的方案!

你说得对,普通的lock锁在异步代码里确实不是好选择:它会直接阻塞线程,浪费线程池资源,完全违背了异步编程“不阻塞、高利用率”的初衷。而AsyncLazy正好解决了异步延迟初始化+全局共享+非阻塞等待这几个核心痛点。

为什么AsyncLazy是最优解?

AsyncLazy的核心逻辑就是:

  • 只有第一次被访问时,才会触发你的初始化逻辑(调用Web服务构造ISomething)
  • 所有后续请求都会异步等待同一个初始化Task完成,不会重复执行构造代码
  • 全程是非阻塞的,线程可以去处理其他请求,直到初始化完成再回来继续

具体实现步骤

1. 定义AsyncLazy(自己实现或用第三方库)

如果不想引入第三方包,可以自己写一个极简版的AsyncLazy:

public class AsyncLazy<T>
{
    private readonly Lazy<Task<T>> _lazyTask;

    public AsyncLazy(Func<Task<T>> factory)
    {
        _lazyTask = new Lazy<Task<T>>(factory);
    }

    public Task<T> Value => _lazyTask.Value;

    // 支持直接await AsyncLazy实例
    public TaskAwaiter<T> GetAwaiter() => Value.GetAwaiter();
}

也可以用成熟的第三方库Nito.AsyncEx里的AsyncLazy,它提供了更多配置选项(比如容错重试),默认行为就是只初始化一次。

2. 注册Singleton的AsyncLazy实例

因为要所有客户端线程共享,必须把AsyncLazy<ISomething>注册为Singleton:

services.AddSingleton(new AsyncLazy<ISomething>(async () =>
{
    // 这里写你的Web服务调用+ISomething构造逻辑
    var httpClient = services.BuildServiceProvider().GetRequiredService<HttpClient>();
    var apiResponse = await httpClient.GetAsync("https://your-service-url/api/data");
    apiResponse.EnsureSuccessStatusCode();
    var dto = await apiResponse.Content.ReadFromJsonAsync<YourDataDto>();
    
    return new SomethingImplementation(dto);
}));

// 你的Scoped Worker正常注册
services.AddScoped<Worker>();

3. 在Scoped Worker中使用AsyncLazy

在每个请求创建的Worker里,注入AsyncLazy<ISomething>,然后直接await获取实例:

public class Worker
{
    private readonly AsyncLazy<ISomething> _sharedSomething;

    public Worker(AsyncLazy<ISomething> sharedSomething)
    {
        _sharedSomething = sharedSomething;
    }

    public async Task ProcessRequestAsync()
    {
        // 异步等待初始化完成,获取全局共享的ISomething实例
        var something = await _sharedSomething;
        
        // 接下来用something处理业务逻辑
        await something.DoBusinessLogicAsync();
    }
}

关键注意事项

  • 必须确保AsyncLazy<ISomething>是Singleton,否则每个Scoped Worker会拿到不同的实例,导致重复初始化
  • 初始化逻辑里如果需要依赖其他服务(比如HttpClient),要正确从服务提供者获取,避免生命周期问题
  • 全程用await等待,绝对不要调用Result或Wait(),否则会回到阻塞线程的老问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:19:17