如何创建全局可用的 WebClient 对象

How to create a globally available WebClient object

我有一个 windows 形式的下载程序,可以下载一堆 URL 的 html 源代码,并提取更多 URL 并下载那些东西。我正在使用

using(WebClient client = new WebClient())
{
    // Do download stuff here...
}

但是 .NET 内存探查器显示严重的内存泄漏,因为很多对象已被垃圾回收但未被处置或被处置但未被垃圾回收。听起来很奇怪,但我的程序有内存泄漏。

要了解我的程序,请考虑以下代码:

private void PreprocessURLs(List<string> URLs)
{
    using(WebClient client = new WebClient())
    {
       // Download first batch of html source code and put in a List<string> property
    }
}

private void ProcessURLs(List<string> URLs)
{
    using(WebClient client = new WebClient())
    {
       // Download more stuff and save them
    }
}

等等。这不是实际的程序,但我想向您展示的是,如何创建一个 "globally" 可用的 WebClient object/instance,这样我就不会创建一堆客户端对象?我想重复使用它。我怀疑这是导致内存泄漏的原因(也许还有其他原因)。也许我错了,因为我从来没有创建过一个程序,其中的任务可能会导致内存泄漏。

编辑:根据 tlemster 的建议,我创建了 WebClient 的静态实例,但这会导致 I/O 错误,因为不支持并发 I/O (DownloadFileAsync())。

EDIT1:这是我的下载方法,它完成主要工作并通过这样做阻止我的 UI 线程。因此我相信内存泄漏来自这个函数。

private void Download()
{
    var stopwatch = new Stopwatch();
    string bla;
    string chapterName;
    string bla1;
    string chapterNumber;
    List<Tuple<string, int>> temp = new List<Tuple<string, int>>();

    // Contains all URLs from preprocessing
    foreach (var chapter in Chapters)
    {
        bla = chapter.Item2;
        chapterName = ReplaceSpecialChars(bla);

        bla1 = chapter.Item3;
        chapterNumber = ReplaceSpecialChars(bla1);

        // Skip this chapter if it already exists based on chapter name
        if (Directory.Exists(string.Format("{0}/{1} - {2}", chapter.Item4, chapterNumber, chapterName)))
        {
            continue;
        }
        else
        {
            Directory.CreateDirectory(string.Format("{0}/{1} - {2}", chapter.Item4, chapterNumber, chapterName));
        }

        // Process each chapter and extract other URLs
        foreach (var item in GetPagesLink(chapter.Item1))
        {
            // Add the extracted URLs to a list for download further down
            temp.Add(new Tuple<string, int>(GetImageLink(item.Item1), item.Item2));
        }

        stopwatch.Start();

        // The download of the files I want after processing it two times
        foreach (var img in temp)
        {
            // A static WebClient does not work because DownloadFileAsync() does not support concurrent I/O
            // Atm should not matter because this whole thing is not async anyway...
            using (WebClient webClient = new WebClient())
            {
                webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
                webClient.DownloadFileAsync(new Uri(img.Item1), string.Format("{0}/{1} - {2}/{3}.jpg", chapter.Item4, chapterNumber, chapterName, img.Item2)); // TODO: Find image type and replace hardcoded jpg
                System.Threading.Thread.Sleep(150);
            }
        }
        stopwatch.Stop();
        temp.Clear();
        txtDebug.AppendText("Finished chapter " + chapter.Item3 + " : " + stopwatch.ElapsedMilliseconds + "\r\n");
        stopwatch.Reset();
    }
    Chapters.Clear();
}

将创建的 WebClient 存储在静态字段中,只要您的程序 运行 或直到您手动删除它,它就会使实例保持活动状态。