C#如何取消预设时间内未完成的异步Task任务?
现有实现存在的问题
- 未真正取消超时任务:当前仅停止等待任务执行,未完成的HTTP请求仍会在后台继续运行,占用网络、内存资源
- 多余的Task.Run封装:
IRCTC_GetFare本身就是返回Task的异步方法,无需用Task.Run再次包装,额外增加线程调度开销 - 闭包参数错误:循环中没有将当前席别
journey的参数传递给查询方法,所有请求会复用同一个全局的LstrJsonData,导致请求参数错误 - 异常风险:直接访问
Task.Result如果任务未正常完成(比如抛出异常)会触发AggregateException,没有做异常处理 - 语法错误:方法声明返回
Task<string>但实际未定义返回值,无法通过编译
更规范的实现方案
核心是用CancellationTokenSource的超时机制,直接向所有异步请求传递取消信号,终止未完成的HTTP请求。
// 方法返回值修改为成功查询到的票价结果列表,符合业务需求 public async Task<List<string>> AvailabilityEnquiry(AvailabilityEnquiry LobjAvailabilityEnquiryRequest) { // 读取超时配置,初始化带超时的取消令牌源 int timeoutSeconds = Convert.ToInt32(ConfigurationManager.AppSettings["TimeOutInSeconds"]); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); List<Task<string>> taskList = new List<Task<string>>(); foreach (var journey in ClassesToGetFares) { // 直接传递当前席别参数和取消令牌 taskList.Add(IRCTC_GetFare(journey, cts.Token)); } try { // 等待所有任务完成或超时触发取消 await Task.WhenAll(taskList); } catch (OperationCanceledException) { // 超时触发取消,无需额外处理,后续直接取已完成的任务结果即可 } // 仅收集正常执行完成的结果,避免访问故障/取消任务的Result抛异常 var completedResults = taskList .Where(t => t.Status == TaskStatus.RanToCompletion) .Select(t => t.Result) .ToList(); return completedResults; } // 修改查询方法,接收席别参数和取消令牌 public static async Task<string> IRCTC_GetFare(JourneyClass journey, CancellationToken cancellationToken) { // 根据当前席别构造请求参数,避免复用全局变量 string LstrJsonData = BuildFareRequestJson(journey); // 将取消令牌传入HTTP请求方法,收到取消信号时直接终止请求 string LstrApiData = await Lobjrestclient.makeRequest_POSTAsync("IRCTC", LstrJsonData, cancellationToken); return "Success"; }
优化点说明
- 超时即触发真实取消:取消令牌会传递到底层HTTP请求,超时后未完成的请求会直接终止,释放资源
- 移除多余Task.Run:直接添加异步方法返回的Task到列表,减少不必要的线程开销
- 消除闭包问题:每个请求单独构造对应席别的参数,避免全局变量复用导致的参数错误
- 异常安全:捕获取消异常,仅读取已成功完成的任务结果,避免未处理异常
- 修正返回值:方法返回所有成功查询到的票价结果列表,符合业务需求
内容的提问来源于stack exchange,提问作者VIGNESH WARAN
相关产品推荐
相关产品推荐

