使用ContinueWith作为“最终”操作是否安全?

考虑一段代码,如:

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

我可以确定WebClient.Dispose()调用是否会发生,并且生成的任何exception都会重新抛出给调用者,就好像没有调用ContinueWith

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

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

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

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

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

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

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

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