【问题标题】:Object disposal and garbage collection prior to event triggering事件触发前的对象处理和垃圾回收
【发布时间】:2016-09-21 06:39:30
【问题描述】:

与我交谈的人提出了一段代码:

private void DownloadInformation(string id)
{
    using (WebClient wc = new WebClient())
    {
        wc.DownloadStringCompleted += 
            new DownloadStringCompletedEventHandler(DownloadStringCompleted);
        wc.DownloadStringAsync(new Uri("http://www.fake.com/" + id));
    }
}

以上是这个的简化版:

(我已获得作者的许可才能发布图片。)

该代码让我困扰的是附加了一个事件处理程序,DownloadStringAsync() 被调用,然后using 块结束,它在WebClient 上调用Dispose()。有什么可以防止WebClientusing 处理掉,甚至在DownloadStringAsync() 完成和DownloadStringCompleted 事件触发之前收集垃圾?

有一个更新的方法,DownloadStringTaskAsync(),我认为可以与 await 结合使用:

private async Task DownloadInformation(string id)
{
    using (WebClient wc = new WebClient())
    {
        wc.DownloadStringCompleted += DownloadStringCompleted;
        await wc.DownloadStringTaskAsync(new Uri("http://www.fake.com/" + id));
    }
}

然而,即便如此......我基本上会打赌事件触发器和处理程序会在 WebClient 被释放之前被调用。

我误解了WebClient 在这种情况下的生命周期还是这是一个糟糕的代码设计?

【问题讨论】:

  • 您不需要DownloadStringTaskAsync 的事件。该任务应包含最终结果。换句话说:var content = await wc.DownloadStringTaskAsync(...);.
  • @LasseV.Karlsen 好点——这肯定会解决这个问题。话虽如此,我肯定对第一个代码 sn-p 感到不安。
  • 第一个例子被严重破坏了。不要使用它。让客户留在身边,直到活动完成。
  • 请注意,最初链接帖子中的代码是正确的 - 否 using...
  • 第一个在stackoverflow.com/questions/979791/…(和rene 的回答)中讨论过,很尴尬,但很好。

标签: c# .net garbage-collection event-handling using-statement


【解决方案1】:

WebClient 没有实现 IDisposable,它的基类 Component 实现了。

Component 类释放任何使用其 Events 属性注册但 WebClient 不使用该属性的事件处理程序。

在 WebClient 上调用 Dispose 不会影响 Webclient 管理的任何状态。

内部资源的实际处理是在私有方法DownloadBits和一个内部类DownloadBitsState中完成的。

因此,由于过早释放资源,您显示的代码到目前为止没有任何影响。然而,这是由实现细节引起的。这些可能会在未来发生变化。

由于框架跟踪待处理的回调,您也不必担心过早的垃圾收集,正如 this answer 中所述,Alexei Levenkov 友情提供。

【讨论】:

  • 除了不实现自己的Dispose() 之外,WebClient 是否有可能在事件触发之前仍会收集垃圾,因为我看不到任何东西让它活着?或者在WebClient 上调用的DownloadStringAsync() 是否使其实例保持活动状态直到完成?该方法返回并且没有任何东西保留对WebClient 实例的引用。
  • 看起来@AlexeiLevenkov 找到了确认您是正确的信息(在 cmets 中)!
【解决方案2】:

该事件仅与DownloadStringAsync 一起使用,而不与DownloadStringTaskAsync 一起使用。

对于后者,任务是Task<string>,当任务完成时,它包含来自下载的响应。

因此第二个例子可以改写成这样:

private async Task DownloadInformation(string id)
{
    using (WebClient wc = new WebClient())
    {
        string response = await wc.DownloadStringTaskAsync(new Uri("http://www.fake.com/" + id));
        // TODO: Process response
    }
}

你完全正确,第一个例子被严重破坏了。在大多数情况下,我希望客户端对象在异步任务完成之前被释放,甚至可能像你提到的那样被垃圾收集。第二个示例,在丢失事件后,没有这些问题,因为它会在释放客户端对象之前正确等待下载完成。

【讨论】:

  • 我也期待 async 关键字
  • 这个答案的第二部分是错误的(“破碎”部分 - 它看起来真的很可怕,因为它看起来很破碎)。更多讨论stackoverflow.com/questions/979791/…
  • 不,您在完成对象之前将其处置不管对象的类型这一事实在这里被严重破坏了。 WebClient 可能工作的事实并不重要。我坚持认为该代码被严重破坏的立场,即使它可能在这种特定情况下起作用。
猜你喜欢
  • 2011-06-21
  • 1970-01-01
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
  • 2010-09-11
  • 1970-01-01
相关资源
最近更新 更多