8

我正在开发 .NET 4.0 中的Web Hook,它将异步运行 lambda,然后在完成后将结果发布到给定的 URI。

我已经让它工作了,但现在我希望 Task 处理抛出的任何异常,并且发现很难阻止它们到达父级。

这是我的代码的一部分:

private readonly Func<T> _startTask;
private readonly string _responseUri;

public Task<T> Begin()
{
    var task = new Task<T>(_startTask);
    task.ContinueWith<T>(End);
    task.Start();
    return task;
}

private T End(Task<T> task)
{
    if (task.IsFaulted)
    {
        return HandleException(task);
    }

    var result = task.Result;
    WebHookResponse.Respond(result, _responseUri);
    return result;
}

private T HandleException(Task<T> task)
{
    WebHookResponse.HandleException(task.Exception.InnerException, _responseUri);
    return null;
}

我尝试过调用ContinueWith()两次以注册一个 continuation to runOnlyOnRanToCompletion和一个 to run的替代版本OnlyOnFaulted。(我不确定调用ContinueWith()两次是否正确。):

public Task<T> Begin()
{
    var task = new Task<T>(_startTask);
    task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion);
    task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted);
    task.Start();
    return task;
}

private T End(Task<T> task)
{
    var result = task.Result;
    WebHookResponse.Respond(result, _responseUri);
    return result;
}

private T HandleException(Task<T> task)
{
    WebHookResponse.HandleException(task.Exception.InnerException, _responseUri);
    return null;
}

所以基本上我想要一种方法让每个任务通过延续函数处理自己的异常。就目前而言,在上述任何一个示例中都不会调用 HandlException 延续函数。

我在测试用例中引起了异常,我应该提到我正在Tasks.WaitAll(tasks);对一组任务进行调用以确保在做出断言之前所有任务都已完成,并且我不确定该调用是否有所作为任务如何处理异常。当前,WaitAll 抛出一个 AggregationException,它聚合每个任务的异常,因为它们没有被 HandleException 延续函数处理。

4

3 回答 3

3

观察任务异常的任务延续不处理异常。它仍然发生在您等待任务完成的任何地方。

你说你在断言之前调用了 WaitAll(tasks) 。我敢打赌,如果你给了足够的时间,你的延续就会运行,但 WaitAll() 上的异常通常会在你的延续运行之前发生。所以你的断言可能在你的延续有机会完成它的工作之前失败了。

于 2012-03-13T01:50:06.090 回答
1

也许,对于 Henk Holterman 的回答,顺序会有所不同。那是,

var task = new Task<T>(_startTask);
task = task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted);
task = task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion);
task.Start();

将确保 HandleException 在必要时运行。

于 2012-02-10T19:41:15.697 回答
1

我使用这种方法是因为它提供了一种很好的声明式流畅编码风格,并且不会在异常处理方面乱扔代码。

class Program
{
    static void Main()
    {
        Task.Factory.StartNew(
            () =>
                {
                    throw new Exception();
                })
            .Success(() => Console.WriteLine("Works"))
            .Fail((ex) => Console.WriteLine("Fails")).Wait();

        Console.ReadKey();
    }
}

public static class TaskExtensions
{
    public static Task Success(this Task task, Action onSuccess)
    {
        task.ContinueWith((t) =>
        {
            if (!t.IsFaulted)
            {
                onSuccess();
            }
        });

        return task;
    }

    public static Task Fail(this Task task, Action<Exception> onFail)
    {
        return task.ContinueWith(
            (t) =>
            {
                if (t.IsFaulted)
                {
                    t.Exception.Handle(x => true);
                    onFail(t.Exception.Flatten());
                }
            });
    }
}
于 2015-10-09T18:28:14.713 回答