C# 这些1k线程来自哪里
我试图创建一个多线程的应用程序,从一个网站下载图像,作为对线程的介绍。(以前从未正确使用过螺纹) 但目前它似乎创建了1000多个线程,我不确定它们来自哪里 我首先将一个线程排队到线程池中,首先,我在作业数组中只有一个作业C# 这些1k线程来自哪里,c#,multithreading,unity3d,.net-3.5,.net-2.0,C#,Multithreading,Unity3d,.net 3.5,.net 2.0,我试图创建一个多线程的应用程序,从一个网站下载图像,作为对线程的介绍。(以前从未正确使用过螺纹) 但目前它似乎创建了1000多个线程,我不确定它们来自哪里 我首先将一个线程排队到线程池中,首先,我在作业数组中只有一个作业 foreach (Job j in Jobs) { ThreadPool.QueueUserWorkItem(Download, j); } 它在一个新线程上启动void下载(objectobj),在该线程中循环一定数量的页面(需要图像/42个图像/页面) 最后传递到
foreach (Job j in Jobs)
{
ThreadPool.QueueUserWorkItem(Download, j);
}
它在一个新线程上启动void下载(objectobj)
,在该线程中循环一定数量的页面(需要图像/42个图像/页面)
最后传递到最后一个函数并下载实际图像
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个图像。您的wc WebLinet将超出范围,并在异步回调之前被随机垃圾收集。此外,在所有异步调用上,您必须允许立即返回和实际的委托函数返回。因此processPage必须位于两个位置。此外,原始循环中的j可能超出范围,具体取决于原始循环中的下载位置 首先,你是如何测量线程数的?为什么你认为你的申请中有上千个呢?您使用的是
ThreadPool
,因此您不能自己创建它们,ThreadPool
也不会根据需要创建这么多
其次,在代码中混合了同步和异步操作。由于您不能使用TPL
和async/await
,让我们检查一下您的代码,并计算您正在创建的工作单元,以便将它们最小化。执行此操作后,ThreadPool
中排队项目的数量将减少,应用程序将获得所需的性能
您没有在应用程序中设置方法,因此:
线程池线程的最大数量
可排队到线程池的操作数仅受可用内存的限制;
但是,线程池限制了可以访问的线程数
同时活跃在这个过程中默认情况下,限制为25
每个CPU的工作线程和1000个I/O完成线程。
因此,必须将最大值设置为5
我在代码中找不到可以检查每个作业的42
图像的位置,您只是在ProcessPage
方法中增加值
检查ManagedThreadId
以获取WebClient.DownloadStringCompleted
的句柄-它是否在不同的线程中执行
您正在ThreadPool
队列中添加新项目,为什么要使用异步操作进行下载?使用一个类似这样的工具:
ProcessPage(wc.DownloadString(downloadLink), false, j);
这将不会在ThreadPool
队列中创建另一个项目,并且这里不会有Sinchronization上下文开关
在ProcessPage
中,您的wc
变量没有被垃圾收集,因此您没有在此处释放所有资源。在此处使用
语句添加:
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
队列中添加项,我认为您可以避免这种情况,也可以使用:
因此,一般来说,避免上下文切换操作,并正确地配置资源。在另一个线程中运行异步操作。为什么不直接使用异步呢?在这种情况下,线程为您提供了什么好处?@Tigran可能没有,只是尝试了解线程的诀窍,在线程情况下使用阻塞调用而不是异步调用是否更有意义?如果您使用异步,请不要使用线程。如果要控制并发工作负载,请使用线程,以便根据需要跨多个线程,而不是更多。VS:Debug->Windows->threads。它应该解释为什么你有这么多线程。(也不知道你为什么贴上unity3d标签——也许有什么特别之处)。@AlexeiLevenkov在unity引擎(游戏引擎)中制作,它的功能比普通的.net有限。
ProcessPage(wc.DownloadString(downloadLink), false, j);
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);
}
}
}
}
}
wc.DownloadFile(url, id + ext);
return true;