.NET Core控制台应用:如何让客户端仅初始化一次并全局复用?
问题
我开发了一个.NET Core控制台应用,在ConfigureServices中通过Singleton作用域注册IHubClient的实现类HubClient。我预期该客户端只会被创建一次,后续所有请求都复用此实例,但实际每次调用await _hubClient.GetClient();时,GetClient方法都会被触发执行。我希望实现服务启动时仅初始化一次客户端,直至应用重启前都复用该实例,请问该如何处理?
服务注册代码
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseWindowsService() .ConfigureServices((_, services) => { services.AddSingleton<IHubClient, HubClient>(x => { var requiredService = x.GetRequiredService<ILogger<HubClient>>(); return new HubClient(requiredService); }); });
IHubClient及HubClient实现代码
public interface IHubClient { public Task GetClient(); } public class HubClient : IHubClient { private readonly ILogger<HubClient> _logger; public HubClient(ILogger<HubClient> logger) { _logger = logger; } public async Task GetClient() { await Task.Delay(1); _logger.LogInformation("gets called every time"); // code to return client return client; } }
调用代码
public class Service : IInvocable { private readonly IHubClient _hubClient; public Service(IHubClient hubClient) { _hubClient = hubClient; } public async Task Invoke() { await _hubClient.GetClient(); } }
解决方案
你的问题核心是Singleton注册的是HubClient实例本身,但每次调用GetClient方法都会重新执行内部逻辑,而非实例被重复创建。要实现客户端仅初始化一次,需要在HubClient内部缓存已创建的客户端实例,后续调用直接复用。
修改步骤:
- 在
HubClient中添加私有字段存储已初始化的客户端实例,同时用线程安全的方式确保仅初始化一次。 - 调整
GetClient方法逻辑:如果实例已存在则直接返回,否则执行初始化逻辑并缓存。 - 可选:如果希望在服务启动时就完成初始化,而非首次调用时才执行,可以在
HubClient的构造函数中触发异步初始化。
修改后的代码示例:
// 假设ClientType是你的客户端实际类型,需替换为真实类型 public interface IHubClient { public Task<ClientType> GetClient(); } public class HubClient : IHubClient { private readonly ILogger<HubClient> _logger; private ClientType _cachedClient; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); // 线程安全控制 public HubClient(ILogger<HubClient> logger) { _logger = logger; // 可选:取消注释后,服务启动时就初始化客户端 // _ = InitializeClientAsync(); } public async Task<ClientType> GetClient() { // 双重检查锁定,兼顾线程安全与性能 if (_cachedClient != null) return _cachedClient; await _semaphore.WaitAsync(); try { if (_cachedClient != null) return _cachedClient; // 原客户端初始化逻辑,仅执行一次 await Task.Delay(1); _logger.LogInformation("only gets called once at first use"); _cachedClient = /* 你的客户端初始化代码 */; } finally { _semaphore.Release(); } return _cachedClient; } // 可选:服务启动时提前初始化的方法 private async Task InitializeClientAsync() { await GetClient(); } }
关键说明:
- 线程安全:使用
SemaphoreSlim和双重检查锁定,避免多线程并发调用时重复初始化客户端。 - 缓存机制:通过
_cachedClient字段存储已初始化的实例,后续调用直接返回缓存值。 - 提前初始化:如果需要在服务启动阶段就完成客户端初始化,可在构造函数中调用
InitializeClientAsync(构造函数无法异步,因此用_ =忽略返回的Task)。
内容的提问来源于stack exchange,提问作者user584018
相关产品推荐
相关产品推荐

