繁体   English   中英

TPL延续中的高效资源使用

[英]Efficient resource usage in TPL continuations

我有兴趣将系统资源与Task Parallel Library和continuation结合使用。

请考虑以下使用另一个最近问题中定义GetResponseAsync()扩展方法的方案。

WebRequest request = HttpWebRequest.Create(uri);
Task<WebResponse> responseTask = request.GetResponseAsync(cancellationToken);
Func<Task<WebResponse>, WebResponse> continuation =
    task =>
    {
        WebRequest followup = HttpWebRequest.Create(uri2);
        return followup.GetResponseAsync(cancellationToken).Result;
    };
Task<WebResponse> finalResultTask = responseTask.ContinueWith(continuation, cancellationToken);

这个配置有很多问题,我想知道如何最好地处理这个问题。 到目前为止我发现的主要项目是:

  1. responseTask核心的执行通过在异步执行期间不阻塞用户线程来有效地使用资源。 但是,由于continuation被定义为Func lambda,执行continuation的线程将在return行上阻塞,直到后续请求的执行完成。 更好的情况是在不阻塞用户线程的同时提供类似继续的行为。

  2. responseTaskfinalResultTask的行为在取消方面有所不同。 如果在操作被取消responseTask正在执行, responseTask会进入状态TaskStatus.Canceled 但是,如果在执行finalResultTask取消操作,则尝试访问Result属性将导致异常,从而导致任务进入TaskStatus.Failed状态。

    • 在失败方面,行为也可能不同。 如果在从continuation返回时尝试访问Result属性时抛出了AggregateException ,则finalResultTask可能有一个双重包装的真内部异常(我不确定此处是否发生特殊情况),其中responseTask.ExceptionInnerException属性responseTask.Exception如果在第一个任务期间失败, responseTask.Exception将提供对实际异常的直接访问。

我怀疑如果您使用Unwrap扩展方法,您的问题都将得到解决。 您的目标是通过延续功能返回新任务; 然而,由于延续本身作为一个任务执行,这将导致嵌套了一层额外的(任务返回任务)。 因此,您需要使用Unwrap消除此嵌套,这将为您提供绑定到内部任务结果的代理任务。

WebRequest request = HttpWebRequest.Create(uri);
Task<WebResponse> responseTask = request.GetResponseAsync(cancellationToken);
Func<Task<WebResponse>, Task<WebResponse> continuation =
    task =>
    {
        WebRequest followup = HttpWebRequest.Create(uri2);
        return followup.GetResponseAsync(cancellationToken);
    };

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation);
Task<WebResponse> proxyTask = finalResultTask.Unwrap();

快速优化:由于您的延续函数除了通过GetResponseAsync生成新任务之外几乎没有其他功能,您可以通过将其指定为ExecuteSynchronously来减少其执行开销:

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation, 
    TaskContinuationOptions.ExecuteSynchronously);

编辑 :Per Servy的建议,你也应该将CancellationToken传递给你的延续函数:

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation, 
    cancellationToken,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Current);

暂无
暂无

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

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