简体   繁体   English

我希望任务处理所有引发的异常,但是发现很难阻止它们到达父级

[英]I want the Task to handle any exceptions that are thrown, but am finding it difficult to stop them from reaching the parent

I'm working on a Web Hook in .NET 4.0 that will run a lambda asynchronously and then post the result to a given URI when it is finished. 我正在.NET 4.0中的Web Hook上工作,该Web Hook将异步运行lambda,然后在完成后将结果发布到给定的URI。

I've got that to work, but now I want the Task to handle any exceptions that are thrown, and am finding it difficult to stop them from reaching the parent. 我已经做到了,但是现在我希望Task处理所有抛出的异常,并且发现很难阻止它们到达父级。

Here's part of my code: 这是我的代码的一部分:

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;
}

An alternative version that I have tried calls ContinueWith() twice to register one continuation to run OnlyOnRanToCompletion and one to run OnlyOnFaulted . 我尝试过的另一种版本调用ContinueWith()两次,以注册一个延续以运行OnlyOnRanToCompletion而另一个则运行OnlyOnFaulted (I'm not sure if calling ContinueWith() twice is correct.): (我不确定两次调用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;
}

So basically I want a way for each Task handle its own exceptions via a continuation function. 因此,基本上,我希望每个任务都可以通过延续函数处理自己的异常。 As it stands the HandlException continuation function is never being called in either of the above examples. 就目前而言,在上述两个示例中都从未调用过HandlException延续函数。

I am causing the exceptions in a test case, and I should mention that I am using a Tasks.WaitAll(tasks); 我在测试用例中引起异常,我应该提到我正在使用Tasks.WaitAll(tasks); call on an array of Tasks to make sure all of the tasks are complete before making my assertions, and I am not sure if that call makes a difference to how exceptions are handled by the Tasks. 在进行我的断言之前,请调用一系列任务以确保所有任务均已完成,并且我不确定该调用是否对任务处理异常的方式有所不同。 Currently WaitAll throws an AggregationException which aggregates the exceptions for each of the Tasks because they aren't being handled by the HandleException continuation function. 当前,WaitAll引发一个AggregationException,该异常会聚合每个Task的异常,因为HandleException连续函数没有处理这些异常。

A task continuation that observes the task's exception doesn't handle the exception. 观察任务异常的任务延续不会处理该异常。 It still happens on wherever you wait on the task to finish. 每当您等待任务完成时,它仍然会发生。

You said you were calling WaitAll(tasks) before asserting. 您说您在断言之前正在调用WaitAll(tasks)。 I'm betting that your continuation would have run if you gave it enough time, but the exception on WaitAll() is usually going to occur before your continuation runs. 我敢打赌,如果您给它足够的时间,您的延续将可以运行,但是WaitAll()上的异常通常会在继续运行之前发生。 So your asserts probably failed before your continuation was given a chance to finish its work. 因此,您的断言可能在您的延续有机会完成其工作之前就失败了。

I use this approach because it provides for a nice declarative fluent coding style and does not litter your code with the exception handling aspects. 我使用这种方法是因为它提供了一种很好的声明性流利的编码风格,并且不会在异常处理方面使您的代码乱七八糟。

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());
                }
            });
    }
}

Perhaps, to the answer of Henk Holterman, the order makes a difference. 也许,对于Henk Holterman的回答, 顺序有所不同。 That is, 那是,

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

would assure that HandleException would run when necessary. 将确保HandleException将在必要时运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM