2012-04-05 16 views
11

Estoy escribiendo un programa de simulación de cliente en el que todo cliente simulado ejecuta alguna rutina predefinida contra el servidor, que es un servidor web ejecutándose en azul con cuatro instancias.Debo usar Subprocesos o Tareas - Multiple Client Simulation

Todos los clientes simulados ejecutan la misma rutina después de conectarse al servidor.

En cualquier momento me gustaría simular de 300 a 800 clientes utilizando mi programa.

Mi pregunta es: ¿Debo crear N instancias de clase de cliente y ejecutarlas en N subprocesos diferentes? O

¿Debo usar la Biblioteca de tareas para hacer las cosas?

+0

[Ver discusión relacionada aquí] (http://stackoverflow.com/questions/10687850/task-factory-startnew-or-parallel-foreach-for-many-long-running-tasks) –

Respuesta

20

Ciertamente no deberías crear 800 hilos.

Demos un paso atrás aquí. Usted tiene un dispositivo llamado "servidor" que recibe "solicitudes" de "clientes" y envía "respuestas" a esos clientes. Supongamos que las solicitudes son hojas de papel entregadas por la oficina de correos, y las respuestas son cajas que contienen libros, también entregados por la oficina de correos.

Desea simular 800 clientes para probar el servidor.

Supongamos que un hilo es una persona y un procesador es una silla. Una persona solo puede hacer trabajo mientras está sentada en la silla.

Crear 800 hilos equivale a salir y contratar a 800 personas, y pagarles a cada uno de ellos para que envíen una carta al servidor. Pero solo tienes cuatro sillas, por lo que esas 800 personas deben turnarse para usar las sillas.

Eso sería una solución absurda en la vida real. Los hilos, como las personas, son increíblemente costoso. Debería minimizar el número de subprocesos que crea.

Entonces, ¿deberías crear 800 tareas a través de la fábrica de tareas y dejar que el TPL las paralelice por ti?

No, tampoco deberías hacer eso. El TPL tiene un grupo de personas (hilos) para dibujar, y trata de arreglar las cosas para que no haya más personas en la nómina que sillas para que se sienten. Pero su tarea no es "presidida por la silla" - - las personas se sentarán en la silla, enviarán la solicitud al servidor y luego se levantarán de la silla mientras esperan que vuelva la respuesta. Mientras esperan, el TPL ahora tiene que contratar a más personas para que realicen tareas adicionales.

Golpear un servidor web está vinculado a E/S; solo debe crear tareas agrupadas por subprocesos para tareas que están vinculadas a la CPU.

La solución correcta es contratar dos personas.

Una persona, el "hilo de finalización de E/S", no hace más que dejar solicitudes en el buzón y comprobar si hay paquetes entrantes. La otra persona, la persona de "simulación", determina cuál es el "horario" correcto para simular 800 clientes. La persona de simulación resuelve el programa y luego se va a dormir. Se despierta cuando es el momento de enviar otra solicitud al servidor. Cuando se despierta, le dice al hilo de finalización de E/S que deje caer esta carta en el buzón y la despierte cuando llegue la respuesta. Luego vuelve a dormirse hasta que sea el momento de enviar otra solicitud o una respuesta. viene que necesita ser verificado.

Lo que debe hacer es (1) obtener la versión beta de C# 5 y usar async/await para crear tareas que envíen solicitudes al servidor y luego devolver el control al bucle de mensajes hasta que sea el momento de enviar otra solicitud o una respuesta entra. O, si no desea utilizar C# 5, debe crear una Fuente de finalización de tarea y configurar las tareas que tienen las continuación correctas.

En resumen: la forma correcta de manejar muchas tareas paralelas de E/S es crear un número muy pequeño de subprocesos, cada uno de los cuales realiza una cantidad muy pequeña de trabajo a la vez. Deje que el subproceso de finalización de E/S maneje los detalles de la E/S. No necesita contratar 800 personas para simular el envío de 800 cartas. Contrate dos personas, una para mirar el buzón y otra para escribir las cartas.

+2

¿Qué pasa si son largos? ejecutando interacciones con el servidor? – Tudor

1

Usaría la biblioteca de tareas y dejaría que la biblioteca de tareas manejara todo el subproceso por usted. No quieres girar 800 hilos. Es una mala idea tener tantos hilos simultáneos funcionando a la vez, aquí hay otra pregunta de desbordamiento de pila que habla de eso: Maximum number of threads in a .NET app?

2

La respuesta en este caso no es tan simple. Realmente depende de cómo desea que sus clientes a ser simulados:

  1. Si usted quiere tener 800 clientes conectados, pero no necesariamente al mismo tiempo, es una buena idea usar Task s. Son livianos y hacen un uso eficiente del subyacente ThreadPool.

  2. Si realmente desea que los clientes sean absolutamente todos paralelos, me temo que no hay manera de evitar realmente los hilos. No hay una forma mágica de obtener 800 tareas de ejecución simultáneas livianas. La abstracción Task es liviana precisamente porque usa el grupo de subprocesos. Esto significa que muchas tareas se asignan a un pequeño número de hilos reales. Pero, por supuesto, esto implica que realmente no se ejecutan en paralelo, sino que están programados para ejecutarse siempre que sea posible. El ThreadPool tiene un número máximo de hilos de 250 (AFAIK), por lo que no se ejecutarán más de 250 "clientes" al mismo tiempo si utiliza Task s. La solución establece subprocesos máximos en 800, pero en este punto es lo mismo que utilizar subprocesos clásicos.

1

Por este application domains son su mejor apuesta.

Un dominio de aplicación es la unidad de tiempo de ejecución de aislamiento en la que se ejecuta una aplicación .NET. Proporciona un límite de memoria administrada, un contenedor para la configuración de la aplicación así como una interfaz de comunicación para aplicaciones distribuidas.

Cada aplicación .NET normalmente aloja solo un dominio de aplicación que se crea automáticamente por el CLR cuando se inicia el proceso/programa dado. En algún momento es útil (en un caso como el suyo) crear dominios de aplicaciones adicionales en un solo proceso/programa. El uso de múltiples dominios de aplicaciones evita complicaciones de comunicación y surgen al utilizar varios procesos individuales y proporciona aislamiento de sus tareas.

Para lo que quieras, tienes dos opciones.

  1. Comience X hilos en una secuencia separada en el mismo dominio.

Esto significa que usted tiene que ser muy cansado de ser seguro para subprocesos, que será muy difícil con una tarea, como la simulación de varios accesos, etc. simular clientes

  1. hilos Inicio X en el mismo proceso, cada uno en su propio dominio de aplicación.

Esto mantendrá a cada uno de los hilos hilados aislados y también de fácil acceso por la aplicación/programa de alojamiento. Al tener todos ustedes X simulación en la X dominios de aplicación separados, cada dominio se aísla y no puede interferir con otra simulación cliente a través de miembros de la clase estática etc.

El siguiente es ayudado por un extracto del libro C# 4.0 In a Nutshell de José Albahari cuales recomiendo encarecidamente que:

Un ejemplo de 40 simulaciones de cliente simultáneas podría ser de utilidad para usted:

class program 
{ 
    static void main() 
    { 
     // Create 40 domains and 40 threads. 
     AppDomain[] domains = new AppDomain[40]; 
     Thread[] thread = new Thread[40]; 

     for (int i = 0; i < 40; i++) 
     { 
      domains[i] = AppDomain.CreateDomain("Client Simulation " + i); 
      thread[i] = new Thread(SimulateClientInOtherDomain); 
     } 

     // Start all threads, passing to each thread its app domain. 
     for (int j = 0; j < 40; j++) 
      threads[j].Start(domains[j]); 

     // Wait for the threads to finish. 
     for (int k = 0; k < 40; k++) 
      threads[k].Join(); 

     // Unload the application domains. 
     for (int l = 0; l < 40; l++) 
      AppDomain.Unload(domains[l]); 
    } 

    // Thread start with input of with domain to run on/in. 
    static void SimulateClientInOtherDomain(object domain) 
    { 
     ((AppDomain)domain).DoCallBack(Simulate); 
    } 

    static void Simulate() 
    { 
     Client simClient1 = new Client("Bill", "Gates", ...); 
     simClient1.Simulate(); 
    } 
} 

espero que esto ayude.