我试图了解处理这个问题的最佳方法是什么,以及如何在TaskWasCancelledException
来取消任务似乎是一个真正的用例。
所以,情节是:我有一个巨大的名单,我希望与3个不同的Regex
匹配。如果我们在Regex
中的任何一个中找到匹配项,我们会将该名称添加到ConcurrentBag
中,并停止将其与其他Regex
匹配。所以我的基本算法是
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());
答案 0 :(得分:1)
除非正则表达式长时间运行,否则无需并行运行3个正则表达式,因为您的名称列表已经并行处理。因此,最好的解决方案是删除正则表达式任务代码。
只需在顺序代码中依次调用正则表达式,并在找到匹配项时停止。这是同时最快和最简单的。
如果您想保留此项,请注意TPL无法取消您的任务代码。您传递的令牌仅在运行任务代码之前检查一次。 .NET代码不可中断。所以取消在这里不可行。
WhenAll
投掷确实是一个必须减轻的问题。你可以说:
Task.WhenAll(myRegexTasks).ContinueWith(_ => { }).Wait();
这会创建一个代理任务,其唯一目的是即使基本任务抛出也不会抛出。不幸的是,.NET Framework仍然没有干净的方法来等待Task
完成而不是抛出。