如何强制在连续任务中观察到任务异常?

发布于 2024-08-21 19:03:11 字数 3038 浏览 3 评论 0原文

我有一个执行 HttpWebRequest 的任务,使用

 Task<WebResponse>.Factory.FromAsync(req.BeginGetRespone, req.EndGetResponse)

它显然会失败并出现 WebException。对于调用者,我想返回一个 Task ,其中 HttpResult 是封装响应(或不封装响应)的帮助程序类型。在这种情况下,4xx 或 5xx 响应不是例外。

因此,我在请求任务中附加了两个延续。一种使用 TaskContinuationOptions OnlyOnRanToCompletion,另一种使用 OnlyOnOnFaulted。然后将整个内容包装在 Task 中,以获取任一延续完成的结果。

三个子任务(请求加上两个延续)中的每一个都是使用 AttachedToParent 选项创建的。

但是当调用者等待返回的外部任务时,如果请求失败,则会抛出 AggregateException 。

我想在 on failed 延续中观察 WebException ,以便客户端代码可以只查看结果。在错误继续抛出中添加 Wait ,但是围绕此问题的 try-catch 没有帮助。查看 Exception 属性(如“使用 Task.Exception 属性观察异常”部分)也没有提示 此处)。

我可以安装一个 UnobservedTaskException 事件处理程序来过滤,但由于该事件没有提供到故障任务的直接链接,因此这可能会在应用程序的这一部分之外进行交互,并且是一个大锤敲碎坚果的情况。

给定一个有故障的 Task 实例,是否有任何方法将其标记为“故障已处理”?

简化的代码:

public static Task<HttpResult> Start(Uri url) {
    var webReq = BuildHttpWebRequest(url);
    var result = new HttpResult();
    var taskOuter = Task<HttpResult>.Factory.StartNew(() => {
        var tRequest = Task<WebResponse>.Factory.FromAsync(
                            webReq.BeginGetResponse,
                            webReq.EndGetResponse,
                            null, TaskCreationOptions.AttachedToParent);
        var tError = tRequest.ContinueWith<HttpResult>(
                            t => HandleWebRequestError(t, result),
                            TaskContinuationOptions.AttachedToParent
                            |TaskContinuationOptions.OnlyOnFaulted);
        var tSuccess = tRequest.ContinueWith<HttpResult>(
                            t => HandleWebRequestSuccess(t, result),
                            TaskContinuationOptions.AttachedToParent
                            |TaskContinuationOptions.OnlyOnRanToCompletion);
        return result;
    });

    return taskOuter;
}

with:

private static HttpDownloaderResult HandleWebRequestError(
                                        Task<WebResponse> respTask, 
                                        HttpResult result) {
    Debug.Assert(respTask.Status == TaskStatus.Faulted);
    Debug.Assert(respTask.Exception.InnerException is WebException);
    // Try and observe the fault: Doesn't help.
    try {
        respTask.Wait();
    } catch (AggregateException e) {
        Log("HandleWebRequestError: waiting on antecedent task threw inner: "
             + e.InnerException.Message);
    }
    // ... populate result with details of the failure for the client ...
    return result;
}

(HandleWebRequestSuccess 最终将派生出进一步的任务来获取响应的内容...)

客户端应该能够等待任务,然后查看其结果,而不需要它由于预期且已处理的错误而抛出。

I have a task to perform an HttpWebRequest using

 Task<WebResponse>.Factory.FromAsync(req.BeginGetRespone, req.EndGetResponse)

which can obviously fail with a WebException. To the caller I want to return a Task<HttpResult> where HttpResult is a helper type to encapsulate the response (or not). In this case a 4xx or 5xx response is not an exception.

Therefore I've attached two continuations to the request task. One with TaskContinuationOptions OnlyOnRanToCompletion and the other with OnlyOnOnFaulted. And then wrapped the whole thing in a Task<HttpResult> to pick up the one result whichever continuation completes.

Each of the three child tasks (request plus two continuations) is created with the AttachedToParent option.

But when the caller waits on the returned outer task, an AggregateException is thrown is the request failed.

I want to, in the on faulted continuation, observe the WebException so the client code can just look at the result. Adding a Wait in the on fault continuation throws, but a try-catch around this doesn't help. Nor does looking at the Exception property (as section "Observing Exceptions By Using the Task.Exception Property" hints here).

I could install a UnobservedTaskException event handler to filter, but as the event offers no direct link to the faulted task this will likely interact outside this part of the application and is a case of a sledgehammer to crack a nut.

Given an instance of a faulted Task<T> is there any means of flagging it as "fault handled"?

Simplified code:

public static Task<HttpResult> Start(Uri url) {
    var webReq = BuildHttpWebRequest(url);
    var result = new HttpResult();
    var taskOuter = Task<HttpResult>.Factory.StartNew(() => {
        var tRequest = Task<WebResponse>.Factory.FromAsync(
                            webReq.BeginGetResponse,
                            webReq.EndGetResponse,
                            null, TaskCreationOptions.AttachedToParent);
        var tError = tRequest.ContinueWith<HttpResult>(
                            t => HandleWebRequestError(t, result),
                            TaskContinuationOptions.AttachedToParent
                            |TaskContinuationOptions.OnlyOnFaulted);
        var tSuccess = tRequest.ContinueWith<HttpResult>(
                            t => HandleWebRequestSuccess(t, result),
                            TaskContinuationOptions.AttachedToParent
                            |TaskContinuationOptions.OnlyOnRanToCompletion);
        return result;
    });

    return taskOuter;
}

with:

private static HttpDownloaderResult HandleWebRequestError(
                                        Task<WebResponse> respTask, 
                                        HttpResult result) {
    Debug.Assert(respTask.Status == TaskStatus.Faulted);
    Debug.Assert(respTask.Exception.InnerException is WebException);
    // Try and observe the fault: Doesn't help.
    try {
        respTask.Wait();
    } catch (AggregateException e) {
        Log("HandleWebRequestError: waiting on antecedent task threw inner: "
             + e.InnerException.Message);
    }
    // ... populate result with details of the failure for the client ...
    return result;
}

(HandleWebRequestSuccess will eventually spin off further tasks to get the content of the response...)

The client should be able to wait on the task and then look at its result, without it throwing due to a fault that is expected and already handled.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

离线来电— 2024-08-28 19:03:11

最后我采取了我能想到的最简单的路线:隐藏异常。这是可能的,因为 WebException 有一个属性 Response,它可以访问我想要的 HttpWebResponse

var requestTask = Task<WebResponse>.Factory.FromAsync(
                        webReq.BeginGetResponse,
                        ia => {
                          try {
                            return webReq.EndGetResponse(ia);
                          } catch (WebException exn) {
                            requestState.Log(...);
                            return exn.Response;
                          }
                        });

然后在延续任务。

In the end I took the simplest route I could think of: hide the exception. This is possible because WebException has a property Response which gives access to the HttpWebResponse I want:

var requestTask = Task<WebResponse>.Factory.FromAsync(
                        webReq.BeginGetResponse,
                        ia => {
                          try {
                            return webReq.EndGetResponse(ia);
                          } catch (WebException exn) {
                            requestState.Log(...);
                            return exn.Response;
                          }
                        });

And then handle errors, redirects and success responses in the continuation task.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文