根据svick的建议,我创建了一个小类,其目的是按顺序运行任务,也就是说,它在ThreadPool上安排它们,但它确保它们按照提交的顺序依次执行。它看起来像这样:
class SequentialTaskRunner<TResult> where TResult : new() {
public Task<TResult> Run(Func<TResult> func) {
var result = RunInternal(func, m_previous);
m_previous = result;
return result;
}
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous) {
await previous;
return await Task.Run(func);
}
Task<TResult> m_previous = Task.FromResult(new TResult());
}
现在,我遇到的问题是如果func()抛出异常,那么Run()的每次后续调用也将返回该异常,并且无法运行新任务。我试着像这样改变RunInternal:
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous) {
if (previous.Exception == null) {
await previous;
}
return await Task.Run(func);
}
但这不能可靠地运作;如果快速提交任务,则一个人的失败仍然可能导致多个人返回相同的异常。我很困惑为什么,并想要一个解释。我刚刚开始使用async / await btw。
答案 0 :(得分:4)
你的代码不起作用的原因是因为你几乎要求它预测未来。
当Task
尚未完成时,其Exception
将始终为null
。这意味着您的await previous
很可能会抛出。
这里最简单的解决方案是使用通用catch
:
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous)
{
try
{
await previous;
}
catch {}
return await Task.Run(func);
}
如果你想隐藏那个空的catch
(因为这通常是一种不好的做法),或者如果你经常这样做并想避免重复,你可以为此编写一个辅助方法:
public static async Task IgnoreException(this Task task)
{
try
{
await task;
}
catch {}
}
用法:
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous)
{
await previous.IgnoreException();
return await Task.Run(func);
}
答案 1 :(得分:0)
也许你可以实现某种队列。
这样,如果一个任务失败,只有该任务将返回异常,并且队列中的下一个任务将完成。并且您将能够处理异常,我不知道,可能再次将任务排队;)
但这只是一个想法!我希望这有帮助!