【问题标题】:Unexpected OutOfMemoryException意外的 OutOfMemoryException
【发布时间】:2012-10-24 18:13:17
【问题描述】:

我曾经有:

using (MyWebClient client = new MyWebClient(TimeoutInSeconds))
{
   var res = client.DownloadData(par.Base_url);
   //code that checks res
}

现在我有:

using (MyWebClient client = new MyWebClient(TimeoutInSeconds))
{
   client.DownloadDataAsync(new Uri(par.Base_url));
   client.DownloadDataCompleted += (sender, e) =>
   {
       //code that checks e.Result
   }
}    

其中 MyWebClient 派生自 WebClient。 现在我有很多线程在做这件事,在第一种情况下内存消耗不是问题,而在第二种情况下,我看到内存稳步上升,直到我得到 OutOfMemoryException。 我进行了分析,似乎 WebClient 是罪魁祸首,没有被处理并保留下载的数据。但为什么?两种情况有什么区别?也许 e.Result 需要以某种方式处理?

【问题讨论】:

    标签: c# memory-management memory-leaks garbage-collection


    【解决方案1】:

    您的第一种情况将并发下载的数量限制为线程数。您的第二种情况对并发下载的数量没有限制。

    【讨论】:

    • 嗯,没错,但我怀疑这是原因,因为在这两种情况下,相同数量的线程都在工作。只是第二种情况并没有让线程一直忙。
    • 在第二种情况下,有多少线程在工作并不重要。这并没有施加限制。只有在第一种情况下,线程数才对并发下载数设置了限制。
    • @ren 在第二个示例中,单个线程可以在第一个请求返回之前触发数十个请求。 每个线程数十个请求比每个线程一个并发请求消耗的内存更多。
    • @Servy 没错,但在我的情况下,每个线程只触发一次下载,并且在两种情况下线程数都是相同的。由于没有快速的“陷阱”答案,我敢打赌我忽略了一些东西。
    • @ren:我想如果你仔细观察,你会发现情况可能并非如此。要确认,请在构造时添加代码以增加原子计数器,并在触发 DownloadDataCompleted 时减少它。我想你会发现柜台比你想象的要高。
    【解决方案2】:

    您将在第二个选项中立即处理您的WebClient。您有两种选择:

    1. 如果您使用的是 .NET 4.5(或安装了 Visual Studio 2012 和 AsyncTargetingPack 的 .NET 4.0),您可以使用 var res = await client.DownloadDataAsync(par.Base_url); 并拥有与您的第一行类似但实际上是异步的代码。
    2. 使用正常的延续并摆脱您的 using

    第一个选项如下所示:

    using (MyWebClient client = new MyWebClient(TimeoutInSeconds))
    {
       var res = await client.DownloadDataAsync(par.Base_url);
       //code that checks res
    }
    

    第二个选项如下所示:

    var client = new MyWebClient(TimeoutInSeconds);
    
    client.DownloadDataAsync(new Uri(par.Base_url))
        .ContinueWith(t =>
        {
            client.Dispose();
    
            var res = t.Result;
    
            //code that checks res
        }
    }  
    

    但是

    您必须根据使用的解决方案更改线程方法。您的代码的第一个版本同步运行,因此如果您有一个专用于 URL 的线程(或连接,或者您正在拆分它们),下载将在该线程上同步运行并阻止它。但是,如果您选择其中任何一个选项,您最终将使用 IO 完成线程来完成您的工作,并将其从主线程中分离出来。从长远来看,这可能更好,但这意味着您必须注意并行提交的这些请求的数量。

    【讨论】:

    • 但这是否意味着我的 using 语句没有以某种方式处理 WebClient 并因此出现问题?
    • @ren 不,这意味着你在真正完成它之前就将其处理掉了。
    • @Servy 但是在完成的事件中尝试使用它时我会遇到异常,不是吗?
    • @ren 这完全取决于它的实现方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-21
    • 2011-09-23
    • 2011-07-14
    • 2012-02-25
    • 2011-05-14
    • 2014-07-21
    • 1970-01-01
    相关资源
    最近更新 更多