我正在尝试创建一个多线程从网站下载图像的应用程序,作为线程的介绍。 (之前从未正确使用过线程)
但目前它似乎创造了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张图像。
答案 0 :(得分:2)
首先,您是如何衡量线程数的?为什么你认为你的应用程序中有数千个?您使用的是ThreadPool
,因此您不能自己创建它们,ThreadPool
也无法根据需要创建大量的广告。1} p>
其次,您在代码中混合了同步和异步操作。由于您无法使用TPL
和async/await
,因此请让您查看代码并计算您正在创建的unit-of-works
,以便最小化它们。执行此操作后,ThreadPool
中排队的项目数量将减少,您的应用程序将获得所需的性能。
您未在应用中设置SetMaxThreads
方法,因此,according the MSDN:
最大线程池线程数
可以排队到线程池的操作数仅受可用内存的限制; 但是,线程池限制了可以的线程数 同时活跃在这个过程中。 默认情况下,限制为25 每个CPU的工作线程和1,000个I / O完成线程。
因此,您必须将最大值设置为5
。
我无法在代码中找到每个作业检查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
方法中,您还使用异步加载。这也会在wc.DownloadFile(url, id + ext);
return true;
队列中添加项目,我认为您可以避免这种情况,并使用synchronious overload:
onclick="return getConfirmation();"
因此,通常,避免上下文切换操作并正确处理资源。
答案 1 :(得分:0)
你的wc WebClinet将超出范围并在异步回调之前被随机垃圾收集。此外,对于所有异步调用,您必须允许立即返回并返回实际的委托函数。所以processPage必须在两个地方。此外,原始循环中的j可能超出范围,具体取决于声明原始循环中的下载位置。