在 WhenAll 抛出 TaskWasCancelled 时取消另一个任务

Cancelling another task while WhenAll throws TaskWasCancelled

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

所以,场景是:我有一个巨大的名字列表,我想匹配 3 个不同的 Regex。如果我们在 Regex 中的任何一个中找到匹配项,我们会将名称添加到 ConcurrentBag 中并停止将其与其他 Regex 匹配。所以我的基本算法是

我的问题出在最后一步,我们正在等待任务完成,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());

除非正则表达式真的很长 运行ning 没有必要 运行 并行处理 3 个正则表达式,因为您的名单已经并行处理。所以最好的解决办法就是删除正则表达式任务代码。

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

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

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

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

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