1

我对结合任务并行库和延续有效地使用系统资源感兴趣。

考虑以下场景,它使用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被定义为Funclambda,所以执行continuation的线程会阻塞return在行上,直到后续请求的执行完成。更好的情况是在不阻塞用户线程的情况下提供类似延续的行为。

  2. 关于取消的行为responseTaskfinalResultTask不同。如果在responseTask执行过程中取消操作,responseTask将进入状态TaskStatus.Canceled。但是,如果在执行过程中取消了操作,那么finalResultTask访问该属性的尝试Result将导致异常,从而导致任务进入TaskStatus.Failed状态。

    • 在失败方面,行为也可能不同。如果在从延续返回AggregateException时尝试访问属性时抛出an ,则可能有一个双重包装的真正内部异常(我不确定这里是否发生特殊情况),其中的属性将提供对实际异常应该在第一个任务期间失败。ResultfinalResultTaskInnerExceptionresponseTask.Exception
4

1 回答 1

3

我怀疑如果您使用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);

编辑:根据Servy的建议,您也应该将您CancellationToken的延续功能传递给您:

Task<Task<WebResponse>> finalResultTask = responseTask.ContinueWith(continuation, 
    cancellationToken,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Current);
于 2013-07-09T13:20:48.817 回答