c# - ¿De dónde vienen estos hilos 1k?
multithreading unity3d (2)
Su wc WebClinet saldrá del alcance y será recogida de basura aleatoriamente antes de la devolución de llamada asincrónica. Además, en todas las llamadas asíncronas debe permitir el retorno inmediato y la función delegada real. Entonces processPage tendrá que estar en dos lugares. Además, la j en el bucle original puede estar fuera del alcance, dependiendo de dónde se declare la descarga en el bucle original.
Intento crear una aplicación que multiplique por hebras la descarga de imágenes desde un sitio web, como una introducción al enhebrado. (Nunca se usó enhebrar correctamente antes)
Pero actualmente parece crear más de 1000 hilos y no estoy seguro de dónde vienen.
Primero cola un hilo en un grupo de hilos, para empezar solo tengo 1 trabajo en el conjunto de trabajos
foreach (Job j in Jobs)
{
ThreadPool.QueueUserWorkItem(Download, j);
}
Lo cual inicia el vacío Download(object obj)
en un nuevo hilo donde pasa por una cierta cantidad de páginas (se necesitan imágenes / 42 imágenes por página)
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);
}
}
}
corrígeme si estoy equivocado, el próximo vacío se llama en el mismo hilo
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);
}
}
}
}
Y finalmente pasa a la última función y descarga la imagen real
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;
}
No estoy seguro de cómo estoy comenzando esta gran cantidad de temas, pero me encantaría saberlo.
Editar
El objetivo de este programa es descargar el trabajo diferente en trabajos al mismo tiempo (máximo de 5) descargando cada uno un máximo de 42 imágenes en ese momento.
por lo que se puede / debe descargar un máximo de 210 imágenes como máximo en todo momento.
Antes que nada, ¿cómo midiste el número de hilos? ¿Por qué crees que tienes miles de ellos en tu aplicación? Estás utilizando ThreadPool
, por lo que no los creas tú solo, y ThreadPool
no crearía una cantidad tan grande de ellos para sus necesidades.
En segundo lugar, está mezclando operaciones sincrónicas y asincrónicas en su código. Como no puede usar TPL
y async/await
, revisemos su código y cuentemos la unit-of-works
que está creando, para poder minimizarlos. Después de hacer esto, la cantidad de elementos en cola en ThreadPool
disminuirá y su aplicación obtendrá el rendimiento que necesita.
No establece el método
SetMaxThreads
en su aplicación, por lo tanto, según MSDN :Número máximo de subprocesos del grupo de subprocesos
El número de operaciones que pueden ponerse en cola para el grupo de subprocesos está limitado solo por la memoria disponible; sin embargo, el grupo de subprocesos limita el número de subprocesos que pueden estar activos en el proceso simultáneamente. De forma predeterminada, el límite es de 25 subprocesos de trabajo por CPU y 1.000 subprocesos de finalización de E / S.Por lo tanto, debe establecer el máximo en
5
.No puedo encontrar un lugar en su código donde verifique las
42
imágenes por trabajo, solo está incrementando el valor en el métodoProcessPage
.- Compruebe
ManagedThreadId
para el identificador deWebClient.DownloadStringCompleted
: se ejecuta en un hilo diferente o no. Está agregando el nuevo elemento en la cola
ThreadPool
, ¿por qué está utilizando la operación asincrónica para descargar? Use una sobrecarga sincrónica , como esta:ProcessPage(wc.DownloadString(downloadLink), false, j);
Esto no creará otro elemento en la cola de
ThreadPool
, y no tendrá un cambio de contexto de sincronización aquí.En
ProcessPage
su variablewc
no se recoge basura, por lo que no está liberando todos sus recursos aquí. Agregar una declaración deusing
aquí: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); } } } } }
En el método
DownloadImage
también usa la carga asincrónica. Esto también agrega elementos en la cola deThreadPoll
, y creo que puede evitar esto y también usar sobrecargas sincrónicas :wc.DownloadFile(url, id + ext); return true;
Por lo tanto, en general, evite las operaciones de cambio de contexto y disponga de sus recursos correctamente.