Task.WhenAll搭配ContinueWith(OnlyOnFaulted)抛出任务取消异常求助
问题原因及解决方案
问题根源
你用TaskContinuationOptions.OnlyOnFaulted指定延续任务只在原任务失败时执行,这会导致原任务成功时,延续任务直接被标记为Canceled状态。而Task.WhenAll会把任何Canceled或Faulted的任务视为整体失败,因此抛出"任务已取消"的异常。
解决方案
方案一:用try/catch包裹异步操作(推荐)
放弃ContinueWith,直接在每个异步任务内部捕获异常,这样不管任务成功还是失败,最终都会处于RanToCompletion状态,Task.WhenAll就能正常完成。
var deleteOldFiles = dbDraft ?.Documents?.Select(s => s.Id) .Select(async id => { try { await _fileStorage.DeleteFileAsync( _fileStorageConfiguration.DefaultFileCollectionName, id, cancellationToken ); } catch (Exception ex) { _logger.LogError(ex, "Could not delete document with identifier: {id}", id); } }); if (deleteOldFiles is not null) await Task.WhenAll(deleteOldFiles);
方案二:调整ContinueWith的逻辑
如果一定要用ContinueWith,不要指定OnlyOnFaulted,而是在延续任务内部判断原任务状态,失败时记录日志,成功时什么都不做。这样延续任务始终会执行并完成,不会出现Canceled状态。
var deleteOldFiles = dbDraft ?.Documents?.Select(s => s.Id) .Select(id => _fileStorage.DeleteFileAsync( _fileStorageConfiguration.DefaultFileCollectionName, id, cancellationToken ) .ContinueWith(t => { if (t.IsFaulted) { _logger.LogError(t.Exception, "Could not delete document with identifier: {id}", id); } // 原任务成功时,延续任务直接完成,无额外操作 }, TaskContinuationOptions.ExecuteSynchronously) ); if (deleteOldFiles is not null) await Task.WhenAll(deleteOldFiles);
补充说明
- 优先选方案一:
async/await的写法比ContinueWith更易读、更符合异步编程的最佳实践,也能避免很多延续任务带来的状态问题。 - 方案二中添加
TaskContinuationOptions.ExecuteSynchronously是为了让延续任务在原任务完成的线程上同步执行,减少线程切换开销,非必须但推荐。
内容的提问来源于stack exchange,提问作者dbuvac
相关产品推荐
相关产品推荐

