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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 05:07:13