c# – 这些1k线程来自哪里

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

但目前它似乎创造了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张图像.

最佳答案 首先,你是如何衡量线程数的?为什么你认为你的应用程序中有数千个?您正在使用ThreadPool,因此您不需要自己创建它们,并且ThreadPool不会为它的需要创建如此大量的它们.

其次,您在代码中混合了同步和异步操作.由于您无法使用TPL和async / await,让我们通过您的代码并计算您正在创建的工作单元,以便最小化它们.执行此操作后,ThreadPool中的排队项目数将减少,您的应用程序将获得所需的性能.

>您没有在应用程序中设置SetMaxThreads方法,因此,according the MSDN

Maximum Number of Thread Pool Threads
The number of operations that can be queued to the thread pool is limited only by available memory;
however, the thread pool limits the number of threads that can be
active in the process simultaneously. By default, the limit is 25
worker threads per CPU and 1,000 I/O completion threads.

所以你必须将最大值设置为5.
>我在代码中找不到每个Job检查42个图像的位置,只是在ProcessPage方法中增加值.
>检查ManagedThreadId以获取WebClient.DownloadStringCompleted的句柄 – 它是否在不同的线程中执行.
>您是在ThreadPool队列中添加新项目,为什么要使用异步操作进行下载?使用synchronious overload,如下所示:

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

这不会在ThreadPool队列中创建另一个项目,并且您不会在此处进行同步上下文切换.
>在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方法中,您还使用异步加载.这也添加了ThreadPoll队列中的项目,我认为你可以避免这种情况,并使用synchronious overload

wc.DownloadFile(url, id + ext);
return true; 

因此,通常,避免上下文切换操作并正确处理您的资源.

点赞