如何让C#文件统计方法后台执行且保持原有int返回类型不变
方案1:同步等待后台任务执行(保留原有阻塞逻辑,仅将执行逻辑转移到线程池线程)
你不需要修改现有方法签名,直接将逻辑包裹在Task.Run中后同步获取结果即可,代码实现如下:
public int GetNumberOfAvatarsInFile() { // 把逻辑放到线程池线程执行,同步等待结果返回 return Task.Run(() => { try { var path = GetAvatarsFilePath(); var numberOfAvatars = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly).Length; return numberOfAvatars; } catch (Exception exception) { var message = $"Error While Getting Total Numbers Of Avatars at {DateTime.Now}\n\t" + $"Error: {JsonConvert.SerializeObject(exception)}"; sentryClient.CaptureMessage(message, SentryLevel.Error); return 1; } }).ConfigureAwait(false).GetAwaiter().GetResult(); }
- 适用场景:对计数实时性要求极高,必须每次调用都返回最新结果
- 注意:调用方依然会阻塞等待IO执行完成,和原有逻辑的耗时没有差异,只是执行逻辑从当前线程转移到了线程池线程,避免占用当前线程的上下文资源(比如ASP.NET Core的请求上下文)
方案2:本地缓存+后台异步更新(无阻塞,调用方瞬间返回)
如果允许计数有短时间的延迟,你可以用本地缓存存当前的计数值,后台异步更新缓存,调用方直接读缓存返回,完全不会被IO阻塞:
// 类字段,存缓存的计数、上次更新时间 private int _cachedAvatarCount = 1; private DateTime _lastUpdateTime = DateTime.MinValue; private readonly object _updateLock = new object(); // 缓存有效期,可根据业务调整,示例为1分钟 private readonly TimeSpan _cacheExpireTime = TimeSpan.FromMinutes(1); public int GetNumberOfAvatarsInFile() { // 缓存未过期直接返回缓存值 if (DateTime.Now - _lastUpdateTime < _cacheExpireTime) { return _cachedAvatarCount; } lock (_updateLock) { // 双重校验,避免多个线程同时触发更新 if (DateTime.Now - _lastUpdateTime < _cacheExpireTime) { return _cachedAvatarCount; } // 第一次调用/缓存过期时先同步查一次返回,后续更新走后台异步 _cachedAvatarCount = GetCountFromDisk(); _lastUpdateTime = DateTime.Now; // 后台异步更新缓存,不阻塞当前调用 _ = Task.Run(async () => { while (true) { await Task.Delay(_cacheExpireTime); var newCount = GetCountFromDisk(); lock (_updateLock) { _cachedAvatarCount = newCount; _lastUpdateTime = DateTime.Now; } } }); return _cachedAvatarCount; } } // 原有计数逻辑抽为单独方法 private int GetCountFromDisk() { try { var path = GetAvatarsFilePath(); return Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly).Length; } catch (Exception exception) { var message = $"Error While Getting Total Numbers Of Avatars at {DateTime.Now}\n\t" + $"Error: {JsonConvert.SerializeObject(exception)}"; sentryClient.CaptureMessage(message, SentryLevel.Error); return 1; } }
- 适用场景:对计数实时性要求不高,允许有和缓存有效期一致的延迟,想要完全避免调用方被IO阻塞
- 注意:计数不是100%实时的,如果头像文件增删频率很高需要对应缩短缓存有效期
内容的提问来源于stack exchange,提问作者Noelia
相关产品推荐
相关产品推荐

