当WhenAll抛出TaskWasCancelled

时间:2018-01-27 10:47:44

标签: c# multithreading asynchronous parallel-processing task-parallel-library

我试图了解处理这个问题的最佳方法是什么,以及如何在TaskWasCancelledException来取消任务似乎是一个真正的用例。

所以,情节是:我有一个巨大的名单,我希望与3个不同的Regex匹配。如果我们在Regex中的任何一个中找到匹配项,我们会将该名称添加到ConcurrentBag中,并停止将其与其他Regex匹配。所以我的基本算法是

  • 并行浏览所有名称
  • 使用Regex.IsMatch
  • 启动三个不同的任务
  • 这三项任务中的每一项都有CancellationToken
  • 只要匹配,就将该项目添加到Bag中并取消其他任务。
  • 等待所有任务完成。

我的问题是在最后一步,等待任务完成,WaitAll抛出TaskWasCancelledException。你能否告诉我我的方法有什么问题?还有什么可以更好地处理这种情况。

另外,我必须检查task != null,我不明白为什么以及某些任务设置为null时的原因。

Parallel.ForEach(accounts, p =>
    {
        var can1 = new CancellationTokenSource();
        var can2 = new CancellationTokenSource();
        var can3 = new CancellationTokenSource();

        tasks.Add(Task.Run(() =>
        {
            if (reg1.IsMatch(p.DisplayName))
            {
                bag.Add(p);
                can2.Cancel();
                can3.Cancel();
            }
        }, can1.Token));

        tasks.Add(Task.Run(() =>
        {
            if (reg2.IsMatch(p.DisplayName))
            {
                bag.Add(p);
                can1.Cancel();
                can3.Cancel();
            }
        }, can2.Token));

        tasks.Add(Task.Run(() =>
        {
            if (reg3.IsMatch(p.DisplayName))
            {
                bag.Add(p);
                can1.Cancel();
                can2.Cancel();
            }
        }, can3.Token));
    }
);

await Task.WhenAll(tasks.Where(t => t != null).ToArray());

1 个答案:

答案 0 :(得分:1)

除非正则表达式长时间运行,否则无需并行运行3个正则表达式,因为您的名称列表已经并行处理。因此,最好的解决方案是删除正则表达式任务代码。

只需在顺序代码中依次调用正则表达式,并在找到匹配项时停止。这是同时最快和最简单的。

如果您想保留此项,请注意TPL无法取消您的任务代码。您传递的令牌仅在运行任务代码之前检查一次。 .NET代码不可中断。所以取消在这里不可行。

WhenAll投掷确实是一个必须减轻的问题。你可以说:

Task.WhenAll(myRegexTasks).ContinueWith(_ => { }).Wait();

这会创建一个代理任务,其唯一目的是即使基本任务抛出也不会抛出。不幸的是,.NET Framework仍然没有干净的方法来等待Task完成而不是抛出。