考虑一段代码,如:
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调用吗?)
答案 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运行是值得你做的。您应该在申请的背景下考虑这个决定。