traves - Subprocesos de límite de grupo de subprocesos de C#
la evaluación de la función requiere que se ejecuten todos los subprocesos. (2)
De acuerdo ... le he dado al sitio una búsqueda justa y he leído muchas publicaciones sobre este tema. Encontré esta pregunta: El código para un grupo de subprocesos simple en C # es especialmente útil.
Sin embargo, como siempre parece, lo que necesito varía levemente.
He revisado el ejemplo de MSDN y lo he adaptado un poco a mis necesidades. El ejemplo al que me refiero está aquí: http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80,printer).aspx
Mi problema es esto. Tengo un conjunto bastante simple de código que carga una página web a través de las clases HttpWebRequest
y WebResponse
y lee los resultados a través de un Stream
. Disparo este método en un hilo, ya que tendrá que ejecutarse muchas veces. El método en sí es bastante corto, pero la cantidad de veces que se debe disparar (con datos variados para cada momento) varía. Puede estar entre 1 y 200.
Todo lo que he leído parece indicar que la clase ThreadPool
es el principal candidato. Aquí es lo que las cosas se ponen difíciles. Podría necesitar despedir esto, decir 100 veces, pero solo puedo tener 3 hilos corriendo como máximo (para esta tarea en particular).
Intenté configurar MaxThreads
en ThreadPool
través de:
ThreadPool.SetMaxThreads(3, 3);
No estoy del todo convencido de que este enfoque funcione. Además, no quiero molestar a otros sitios web o programas que se ejecutan en el sistema en el que se ejecutará. Entonces, al limitar el número de hilos en ThreadPool
, ¿puedo estar seguro de que esto pertenece solo a mi código y mis hilos?
El ejemplo de MSDN utiliza el enfoque de unidad de evento y llama a WaitHandle.WaitAll(doneEvents);
que es como estoy haciendo esto
Entonces, el corazón de mi pregunta es, ¿cómo se puede garantizar o especificar un número máximo de hilos que se pueden ejecutar para su código, pero que el código siga ejecutando más hilos a medida que los anteriores terminan hasta algún punto arbitrario? ¿Estoy abordando esto de la manera correcta?
Sinceramente,
Jason
De acuerdo, agregué un enfoque de semáforo y ThreadPool
completo el código de ThreadPool
. Parece lo suficientemente simple. Obtuve mi información de: http://www.albahari.com/threading/part2.aspx
Es este ejemplo el que me mostró cómo:
[El texto a continuación aquí es una copia / pega desde el sitio]
Un Semaphore
con una capacidad de uno es similar a un Mutex
o un lock
, excepto que el Semaphore
no tiene un "propietario", es un hilo agnóstico. Cualquier hilo puede llamar a Release
en un Semaphore
, mientras que con Mutex
y lock
, solo el hilo que obtuvo el recurso puede liberarlo.
En el siguiente ejemplo, diez subprocesos ejecutan un bucle con una instrucción Sleep
en el medio. Un Semaphore
asegura que no más de tres subprocesos pueden ejecutar esa declaración de Sleep
a la vez:
class SemaphoreTest
{
static Semaphore s = new Semaphore(3, 3); // Available=3; Capacity=3
static void Main()
{
for (int i = 0; i < 10; i++)
new Thread(Go).Start();
}
static void Go()
{
while (true)
{
s.WaitOne();
Thread.Sleep(100); // Only 3 threads can get here at once
s.Release();
}
}
}
Es una clase estática como cualquier otra, lo que significa que cualquier cosa que hagas con ella afecta a todos los hilos en el proceso actual. No afecta otros procesos.
Sin embargo, considero que es uno de los defectos de diseño más grandes en .NET. ¿A quién se le ocurrió la brillante idea de hacer que el grupo de subprocesos estético ? Como muestra su ejemplo, a menudo queremos un grupo de subprocesos dedicado a nuestra tarea, sin que interfiera con tareas no relacionadas en ningún otro lugar del sistema.
Nota: si está limitando esto a "3" para que no abrume a la máquina que ejecuta su aplicación, me aseguraré de que esto sea un problema primero. The threadpool se supone que debe administrar esto por ti. Por otro lado, si no quieres abrumar a algún otro recurso, ¡sigue leyendo!
No puede administrar el tamaño del grupo de subprocesos (o realmente mucho de nada al respecto).
En este caso, usaría un semáforo para administrar el acceso a su recurso. En su caso, su recurso está ejecutando el raspado web, o calculando algún informe, etc.
Para hacer esto, en tu clase estática, crea un objeto de semáforo:
System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3);
Entonces, en cada hilo, haces esto:
System.Threading.Semaphore S = new System.Threading.Semaphore(3, 3);
try
{
// wait your turn (decrement)
S.WaitOne();
// do your thing
}
finally {
// release so others can go (increment)
S.Release();
}
Cada hilo se bloqueará en S.WaitOne () hasta que reciba la señal para continuar. Una vez que S se ha decrementado 3 veces, todos los hilos se bloquearán hasta que uno de ellos incremente el contador.
Esta solución no es perfecta.
Si quieres algo un poco más limpio y más eficiente, te recomendaría ir con un enfoque BlockingQueue en el que encola el trabajo que deseas realizar en un objeto global de cola de bloqueo.
Mientras tanto, tienes tres hilos (que creaste, no en el grupo de hilos), sacando el trabajo de la cola para que funcione. Esto no es tan complicado de configurar y es muy rápido y simple.
Ejemplos: