.NET:我是否需要在异步下载时保留对WebClient的引用?

时间:2009-06-11 07:32:24

标签: c# .net asynchronous garbage-collection webclient

我在一段生产代码中使用以下方法:

private void DownloadData(Uri uri)
{
    WebClient webClient = new WebClient();
    DownloadDataCompletedEventHandler eh = null;
    eh = delegate(object sender, DownloadDataCompletedEventArgs e)
        {
            webClient.DownloadDataCompleted -= eh;
            ((IDisposable) webClient).Dispose();
            OnDataDownloaded();
        };
    webClient.DownloadDataCompleted += eh;
    webClient.DownloadDataAsync(uri);
}

我现在担心,在调用WebClient事件之前,DownloadDataCompleted实例被垃圾收集可能导致难以重现的错误:退出DownloadData()方法后,有没有对WebClient对象的明显引用,因此可能会发生这种情况。

所以我的问题是:这真的可以实现吗?我无法重现该问题,因此可能会发生一些内部事情阻止WebClient对象被垃圾收集(例如,对象可能在等待响应时在某处注册自己的全局对象)。

代码在.NET 2.0上运行,如果这有任何区别。

5 个答案:

答案 0 :(得分:12)

不,在回调完成之前,您的对象不会被GC编辑。根据{{​​3}},“异步API保留对您的请求的引用(在提交异步IO操作的线程池中),因此在完成之前不会对其进行垃圾回收。

但是,你的代码也在做它不需要的东西:你不需要分离事件处理程序而不需要在webclient上调用Dispose。 (Dispose()实际上并未由WebClient实现 - 您可以在Does the Garbage Collector destroy temporarily unreferenced objects during async calls in .NET?)的.NET Framework参考源中看到这一点。

因此,您实际上不需要在回调中引用webclient实例。换句话说,以下代码也可以正常工作,并避免从委托内部引用外部局部变量的任何潜在问题(如上所述)。

private void DownloadData(Uri uri)
{
    WebClient webClient = new WebClient();
    DownloadDataCompletedEventHandler eh = null;
    eh = delegate(object sender, DownloadDataCompletedEventArgs e)
    {
        OnDataDownloaded();
    };
    webClient.DownloadDataCompleted += eh;
    webClient.DownloadDataAsync(uri);
}

无论如何,你可能想在别处寻找你的bug来源。我看到的一个地方是HTTP调用的结果 - 你可能正在耗尽内存,可能会遇到服务器错误等。你可以查看e.Error来查看调用是否真的有效。

答案 1 :(得分:5)

在异步操作正在进行时,我不确定WebClient是否通常可以进行垃圾回收,因为可能存在内部引用 - 但更大的问题是:它是否重要?< / p>

只要有足够的WebClient保持“活着”来为请求提供服务并调用你的处理程序,主WebClient对象本身是否被垃圾回收是否重要?

WebClient文档没有提到任何关于必须保留引用的内容(例如,与System.Threading.Timer docs不同)所以我认为可以合理地假设这是可以的。

在这种特殊情况下,您的委托具有对WebClient的引用,因此只要引用了委托本身,WebClient就不能引用。我有根据的猜测是系统某处的某些部分需要进行回调才能知道当网络流量到达时该做什么,并且该回调最终会(间接地)导致您的代表,所以你是好的。

答案 2 :(得分:1)

您可以尝试使用Windows调试工具调试应用程序 - 它允许您查看保留对特定对象(with the appropriate plug-in)的引用的确切内容。对这种情况非常有用。

但是,我不知道你问题的答案。一种可能性是,在操作期间,WebClient使自己成为“根”对象之一,它永远不会被垃圾收集(.NET应用程序通常有大约5到10个这样的对象,它们是使用的几个引用树的根由申请)。不过,这是纯粹的推测。

答案 3 :(得分:0)

硬币的翻转......如果您在某处存储了对WebClient的引用,只是为了看看它是否有所作为......这是否会让问题完全消失?以这种方式检查它可能更容易,并且确定不要猜测看似合乎逻辑的东西。

答案 4 :(得分:0)

创建一个带有范围变量引用的匿名方法(在你的例子中是webClient),并使用对该对象的引用使它成为自己的变量。因此,当jon猜测你的委托将持有对webClient的引用时,在委托取消注册它之前,webClient不能被垃圾收集。

但是我通常建议不要在委托方法中使用webClient引用,而是将发送者强制转换为内部变量。在匿名方法外部使用变量可能会导致一些非常奇怪的错误。