在 WhenAll 抛出 TaskWasCancelled 时取消另一个任务
Cancelling another task while WhenAll throws TaskWasCancelled
我试图了解处理这个问题的最佳方法是什么,以及我该如何克服 TaskWasCancelledException
取消任务似乎是一个真正的用例。
所以,场景是:我有一个巨大的名字列表,我想匹配 3 个不同的 Regex
。如果我们在 Regex
中的任何一个中找到匹配项,我们会将名称添加到 ConcurrentBag
中并停止将其与其他 Regex
匹配。所以我的基本算法是
- 并行遍历所有名称
- 使用 Regex.IsMatch
开始三个不同的任务
- 这三个任务中的每一个都有
CancellationToken
- 一旦有匹配项,将项目添加到包中并取消 其他任务。
- 等待所有任务完成。
我的问题出在最后一步,我们正在等待任务完成,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
完成但不抛出。
我试图了解处理这个问题的最佳方法是什么,以及我该如何克服 TaskWasCancelledException
取消任务似乎是一个真正的用例。
所以,场景是:我有一个巨大的名字列表,我想匹配 3 个不同的 Regex
。如果我们在 Regex
中的任何一个中找到匹配项,我们会将名称添加到 ConcurrentBag
中并停止将其与其他 Regex
匹配。所以我的基本算法是
- 并行遍历所有名称
- 使用 Regex.IsMatch 开始三个不同的任务
- 这三个任务中的每一个都有
CancellationToken
- 一旦有匹配项,将项目添加到包中并取消 其他任务。
- 等待所有任务完成。
我的问题出在最后一步,我们正在等待任务完成,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
完成但不抛出。