这些1k线程来自哪里

时间:2015-05-11 19:34:14

标签: c# multithreading unity3d .net-3.5 .net-2.0

我正在尝试创建一个多线程从网站下载图像的应用程序,作为线程的介绍。 (之前从未正确使用过线程)

但目前它似乎创造了1000多个线程,我不确定它们的来源。

我首先将一个线程排入一个线程池,对于初学者我在jobs数组中只有一个作业

foreach (Job j in Jobs)
{
    ThreadPool.QueueUserWorkItem(Download, j);
}

在一个新线程上启动void Download(object obj),它会遍历一定数量的页面(需要图像/每页42个图像)

for (var i = 0; i < pages; i++)
{
    var downloadLink = new System.Uri("http://www." + j.Provider.ToString() + "/index.php?page=post&s=list&tags=" + j.Tags + "&pid=" + i * 42);

    using (var wc = new WebClient())
    {
        try
        {
            wc.DownloadStringAsync(downloadLink);
            wc.DownloadStringCompleted += (sender, e) =>
            {
                response = e.Result;  
                ProcessPage(response, false, j);
            };
        }
        catch (System.Exception e)
        {
            // Unity editor equivalent of console.writeline
            Debug.Log(e);
        }
    }
}

如果我错了,请纠正我,在同一个线程上调用下一个void

void ProcessPage(string response, bool secondPass, Job j)
{
    var wc = new WebClient();
    LinkItem[] linkResponse = LinkFinder.Find(response).ToArray();

    foreach (LinkItem i in linkResponse)
    {
        if (secondPass)
        {
            if (string.IsNullOrEmpty(i.Href))
                continue;
            else if (i.Href.Contains("http://loreipsum."))
            {
                if (DownloadImage(i.Href, ID(i.Href)))
                    j.Downloaded++;
            }
        }
        else
        {
            if (i.Href.Contains(";id="))
            {
                var alterResponse = wc.DownloadString("http://www." + j.Provider.ToString() + "/index.php?page=post&s=view&id=" + ID(i.Href));
                ProcessPage(alterResponse, true, j);
            }
        }
    }
}

最后传递给最后一个函数并下载实际图像

bool DownloadImage(string target, int id)
{
    var url = new System.Uri(target);
    var fi = new System.IO.FileInfo(url.AbsolutePath);
    var ext = fi.Extension;

    if (!string.IsNullOrEmpty(ext))
    {
        using (var wc = new WebClient())
        {
            try
            {
                wc.DownloadFileAsync(url, id + ext);
                return true;
            }
            catch(System.Exception e)
            {
                if (DEBUG) Debug.Log(e);
            }
        }
    }
    else
    {
        Debug.Log("Returned Without a extension: " + url + " || " + fi.FullName);
        return false;
    }
    return true;
}

我不知道我是如何开始这么多线程的,但我很想知道。

修改

这个程序的目标是同时下载不同的作业(最多5个),每个下载最多42个图像。

所以最多可以/应该最多下载210张图像。

2 个答案:

答案 0 :(得分:2)

首先,您是如何衡量线程数的?为什么你认为你的应用程序中有数千个?您使用的是ThreadPool,因此您不能自己创建它们,ThreadPool也无法根据需要创建大量的广告。

其次,您在代码中混合了同步和异步操作。由于您无法使用TPLasync/await,因此请让您查看代码并计算您正在创建的unit-of-works,以便最小化它们。执行此操作后,ThreadPool中排队的项目数量将减少,您的应用程序将获得所需的性能。

  1. 您未在应用中设置SetMaxThreads方法,因此,according the MSDN

      

    最大线程池线程数
      可以排队到线程池的操作数仅受可用内存的限制;   但是,线程池限制了可以的线程数   同时活跃在这个过程中。 默认情况下,限制为25   每个CPU的工作线程和1,000个I / O完成线程。

    因此,您必须将最大值设置为5

  2. 我无法在代码中找到每个作业检查42张图片的位置,只是在ProcessPage方法中增加值。

    < / LI>
  3. 检查ManagedThreadId是否为WebClient.DownloadStringCompleted的句柄 - 是否在不同的线程中执行。
  4. 您正在ThreadPool队列中添加新项目,为什么要使用异步操作进行下载?使用synchronious overload,如下所示:

    ProcessPage(wc.DownloadString(downloadLink), false, j);
    

    这不会在ThreadPool队列中创建另一个项目,并且您不会在此处进行同步上下文切换。

  5. ProcessPage wc变量中,您的using变量不会被垃圾收集,因此您无需在此处释放所有资源。在此处添加void ProcessPage(string response, bool secondPass, Job j) { using (var wc = new WebClient()) { LinkItem[] linkResponse = LinkFinder.Find(response).ToArray(); foreach (LinkItem i in linkResponse) { if (secondPass) { if (string.IsNullOrEmpty(i.Href)) continue; else if (i.Href.Contains("http://loreipsum.")) { if (DownloadImage(i.Href, ID(i.Href))) j.Downloaded++; } } else { if (i.Href.Contains(";id=")) { var alterResponse = wc.DownloadString("http://www." + j.Provider.ToString() + "/index.php?page=post&s=view&id=" + ID(i.Href)); ProcessPage(alterResponse, true, j); } } } } } 语句:

    DownloadImage
  6. ThreadPoll方法中,您还使用异步加载。这也会在wc.DownloadFile(url, id + ext); return true; 队列中添加项目,我认为您可以避免这种情况,并使用synchronious overload

    onclick="return getConfirmation();"
  7. 因此,通常,避免上下文切换操作并正确处理资源。

答案 1 :(得分:0)

你的wc WebClinet将超出范围并在异步回调之前被随机垃圾收集。此外,对于所有异步调用,您必须允许立即返回并返回实际的委托函数。所以processPage必须在两个地方。此外,原始循环中的j可能超出范围,具体取决于声明原始循环中的下载位置。