我正在尝试创建一个多线程从网站下载图像的应用程序,作为线程的介绍. (之前从未正确使用过线程)
但目前它似乎创造了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;
因此,通常,避免上下文切换操作并正确处理您的资源.