任务被取消,但不清楚原因和方式

Tasks are getting cancelled, but it's not clear why and how

我正在 运行 在 ASP.NET 框架上开发一个大型单体 Web 应用程序并添加了一个功能。我构建了一些抽象,以便 运行 并行执行多个任务。

public interface IImporter
{
    Action[] GetProcessActions();
}

public class ImportTaskFactory : IDisposable
{
    private readonly Task[] _tasks;

    /// <summary>
    /// Immediately starts all the Process tasks
    /// </summary>
    public ImportTaskFactory(CancellationToken cancellationToken, Action<Task> onTaskFaultedAction, IEnumerable<IImporter> importers)
    {
        _tasks = importers
            .SelectMany(importer =>
                importer
                    .GetProcessActions()
                    .Select(action =>
                        Task.Factory.StartNew(action, cancellationToken)
                                    .ContinueWith(onTaskFaultedAction, TaskContinuationOptions.OnlyOnFaulted)))
            .ToArray();
    }

    public void WaitAll(CancellationToken cancellationToken)
    {
        Task.WaitAll(_tasks, cancellationToken);
    }

    public void Dispose()
    {
        foreach (var task in _tasks)
        {
            task?.Dispose();
        }
    }
}

它被称为

//...
var importers = new IImporter[]
{
   //...three classes deriving from IImporter
}
using (var importTasks =
    new ImportTaskFactory(_cancellationTokenSource.Token, OnTaskFaulted, importers))
    {
        importTasks.WaitAll(_cancellationTokenSource.Token);
    }
//... where:
    private void OnTaskFaulted(Task task)
    {
        Log.Logline(task.Exception.ToString());
        _cancellationTokenSource.Cancel();
    }
//...

然而,当 运行 任务开始时很好并完成它们的工作,但似乎由于不明原因而停止,每个任务都抛出一个 System.Threading.Tasks.TaskCanceledException(没有内部异常)。 如果我查看传递的 CancellationToken,未请求取消。

几个小时以来我一直在尝试对此进行调试,但不知道发生了什么。如何以这种方式取消任务?我该如何调试呢?我的意思是我怎样才能看到是什么触发了取消?

我在 OnTaskFaulted 方法中设置了断点,但它似乎从未被调用...那是唯一调用 _cancellationTokenSource.Cancel(); 的点。

这不是您问题的答案。它只是展示了如何做你想做的事,而不会出现这些讨厌的取消异常:

void ImportTaskFactory(CancellationToken cancellationToken,
    Action<Task> onTaskFaultedAction, IEnumerable<IImporter> importers)
{
    _tasks = importers
        .SelectMany(importer => importer
            .GetProcessActions()
            .Select(action => Task.Run(() =>
            {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    onTaskFaultedAction(Task.FromException(ex));
                }
            }, cancellationToken))).ToArray();
}

我将 Task.Factory.StartNew 替换为 Task.Run, because the former requires,以便在您每次使用它时显式传递 scheduler

如果 onTaskFaultedAction 参数的类型是 Action<Exception> 而不是 Action<Task>.

可能会更简单