顺序运行任务:如何处理异常?

时间:2014-03-08 18:07:22

标签: c# async-await

根据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。

2 个答案:

答案 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)

也许你可以实现某种队列。

这样,如果一个任务失败,只有该任务将返回异常,并且队列中的下一个任务将完成。并且您将能够处理异常,我不知道,可能再次将任务排队;)

但这只是一个想法!我希望这有帮助!