将ContinueWith用作"最后"是否安全?操作?

时间:2014-09-23 21:16:43

标签: c# .net task-parallel-library

考虑一段代码,如:

private Task<string> Download()
{
    var wc = new WebClient();
    Task<string> backgroundDownload = wc.DownloadStringTaskAsync(this.Uri);
    // Make sure the WebClient is disposed no matter what.
    backgroundDownload.ContinueWith((downloadTask) => { wc.Dispose(); });
    return backgroundDownload;
}

我是否可以确定WebClient.Dispose()调用是否已发生,并且生成的任何异常都会重新发送给调用方,就好像没有调用ContinueWith一样?

客户可以观察ContinueWith吗? (例如稍后会调用ContinueWith删除Dispose调用吗?)

2 个答案:

答案 0 :(得分:11)

使用您拥有的代码,您可以确定无论代码是否成功完成,取消或引发异常,都将触发延续。

使用您所拥有的解决方案的一个潜在的问题是,在处理Web客户端之前,期间或之后,可能会运行其他延续。如果您在此清理运行之前没有运行其他延续的问题,那么您拥有的就可以了。如果这是一个问题,那么您需要返回继续,而不是原始任务,但您还需要正确传播结果(和例外/取消)。 async的使用使这一切更容易:

private async Task<string> Download()
{
    using(var wc = new WebClient())
      return await wc.DownloadStringTaskAsync(this.Uri);
}

答案 1 :(得分:0)

首先,即使发生常规异常,也会执行继续。但是,如果出现异常情况(例如OutOfMemoryException),它比常规finally块更不可能运行。

现在我不会尝试处理webclient。请记住,处理是一种优化,因为本机资源无论如何都将由终结器处理。我们处理的唯一原因是终结器很昂贵,因为它会触发第二次GC传递。

但是为了执行优化,系统可能必须创建新线程。此外,如果线程池中充满了长时间运行的任务,那么你可能会大大延长webclient的生命周期。

基本上,你必须选择两个邪恶中较小的一个,我不相信一个较少的GC运行是值得你做的。您应该在申请的背景下考虑这个决定。