2012-04-05 2 views
11

Sto scrivendo un programma di simulazione client in cui tutti i client simulati eseguono alcune routine predefinite contro il server - che è un server Web in esecuzione in azzurro con quattro istanze.Devo usare Thread o Task - Simulazione di più client

Tutti i client simulati eseguono la stessa routine dopo essersi connessi al server.

In qualsiasi momento vorrei simulare da 300 a 800 client utilizzando il mio programma.

La mia domanda è: Devo creare N istanze di classe client ed eseguirle in N thread differenti? OR

Devo utilizzare la libreria di attività per eseguire le operazioni?

+0

[Vedi discussione correlata qui] (http://stackoverflow.com/questions/10687850/task-factory-startnew-or-parallel-foreach-for-many-long-running-tasks) –

risposta

20

Non si dovrebbe certamente creare 800 thread.

Facciamo un passo indietro qui. Hai un dispositivo chiamato "server" che accetta "richieste" da "clienti" e invia "risposte" a quei client. Supponiamo che le richieste siano pezzi di carta consegnati dall'ufficio postale e che le risposte siano scatole contenenti libri, anche consegnate dall'ufficio postale.

Si desidera simulare 800 client per testare il server.

Supponiamo che un thread sia una persona e che un processore sia una sedia. Una persona può solo lavorare mentre è seduta sulla sedia.

Creare 800 thread equivale ad uscire e assumere 800 persone e pagare ciascuno di essi per inviare una lettera al server. Ma hai solo quattro sedie, quindi quelle 800 persone devono fare a turno usando le sedie.

Sarebbe una soluzione ridicola nella vita reale. Thread, come le persone, sono follemente costoso. Dovresti ridurre al minimo il numero di thread che crei.

Quindi, dovresti invece creare 800 attività tramite la task factory e lasciare che TPL le parallelizzi per te?

No, non dovresti farlo neanche. Il TPL ha un gruppo di persone (discussioni) da cui attingere, e cerca di sistemare le cose in modo che non ci siano più persone sul libro paga di quante sedie ci siano per loro. Ma il tuo compito non è "legato alla sedia" - - le persone si siederanno sulla sedia, invieranno la richiesta al server, e poi si alzeranno dalla sedia mentre aspettano che la risposta torni. Mentre stanno aspettando, la TPL ora deve assumere più persone per assistere le attività aggiuntive.

Il colpire un server Web è legato all'I/O; è necessario creare solo attività con pool di thread per attività associate alla CPU.

La soluzione giusta è quella di assumere due persone.

Una persona - il "thread di completamento I/O" - non fa altro che eliminare le richieste nella casella di posta e controllare i pacchetti in arrivo. L'altra persona - la persona della "simulazione" - elabora qual è il giusto "programma" per la simulazione di 800 clienti. La persona di simulazione elabora il programma e poi si addormenta. Si sveglia quando è ora di inviare un'altra richiesta al server. Quando si sveglia, dice al thread di completamento dell'I/O di rilasciare questa lettera nella casella di posta e svegliarla quando la risposta arriva. Quindi torna a dormire finché non è il momento di inviare un'altra richiesta o una risposta arriva che deve essere verificato.

Quello che dovresti fare è (1) ottenere la versione beta di C# 5 e usare async/await per creare attività che inviano richieste al server, e quindi restituire il controllo al ciclo dei messaggi fino a quando non è il momento di inviare un altro richiesta o una risposta arriva. O, se non si desidera utilizzare C# 5, è necessario creare una fonte di completamento attività e impostare attività che hanno le giuste continuazioni.

In breve: il modo corretto per gestire molti compiti di I/O paralleli è creare un numero molto piccolo di thread, ognuno dei quali esegue una quantità minima di lavoro alla volta. Lascia che il thread di completamento I/O gestisca i dettagli dell'I/O. Non è necessario assumere 800 persone per simulare l'invio di 800 lettere. Noleggio due persone, uno per guardare la cassetta postale e uno per scrivere le lettere.

+2

Cosa succede se sono lunghi in esecuzione interazioni con il server? – Tudor

1

Vorrei utilizzare la libreria delle attività e lasciare che la libreria delle attività gestisca tutto il threading per te. Non vuoi far girare 800 thread. È una cattiva idea quella di avere tanti thread simultanei alla volta, ecco un'altra domanda di overflow dello stack che parla di questo: Maximum number of threads in a .NET app?

2

La risposta in questo caso non è così semplice. Dipende molto da come volete che i vostri clienti da simulare:

  1. Se si vuole avere 800 clienti collegati, ma non necessariamente, allo stesso tempo, è una buona idea usare Task s. Sono leggeri e fanno un uso efficiente del sottostante ThreadPool.

  2. Se si desidera che i client siano assolutamente tutti in parallelo, temo che non ci sia modo di evitare effettivamente i thread. Non esiste un modo magico per ottenere 800 attività di esecuzione simultanee e leggere. L'astrazione Task è leggera precisamente perché utilizza il pool di thread. Ciò significa che molte attività sono associate a un numero limitato di thread effettivi. Ma, ovviamente, questo implica che non funzionano veramente in parallelo, ma sono invece programmati per funzionare quando possibile. ThreadPool ha un numero massimo di thread di 250 (AFAIK), quindi non verranno eseguiti più di 250 "client" contemporaneamente se si utilizza Task s. La soluzione è impostata su max thread a 800, ma a questo punto è come utilizzare i thread classici.

1

Per questo application domains è la soluzione migliore.

Un dominio di applicazione è l'unità di isolamento in cui viene eseguita un'applicazione .NET. Fornisce un limite di memoria gestita, un contenitore per le impostazioni di configurazione dell'applicazione e fornisce un'interfaccia di comunicazione per applicazioni distribuite.

Ogni applicazione .NET di solito ospita un solo dominio di applicazione che viene creata automaticamente dal CLR quando il dato processo/programma. È talvolta utile (in un caso come il tuo) creare domini applicativi aggiuntivi con un singolo processo/programma. L'utilizzo di più domini applicativi evita complicazioni di comunicazione e si manifesta utilizzando diversi processi individuali e fornisce l'isolamento delle attività.

Per quello che si vuole si hanno due opzioni.

  1. Avvia thread X su thread separati nello stesso dominio.

Ciò significa che si dovrà essere molto stanco di essere thread-safe, che sarà molto difficile con un tale compito, come la simulazione di login multipli, simulare i clienti ecc

  1. discussioni avviare X nello stesso processo ciascuno nel proprio dominio dell'applicazione.

Ciò manterrà isolati tutti i thread di spun e facilmente accessibili dall'applicazione/programma di hosting. Avendo tutti voi X simulazione in X domini applicativi separati, ogni dominio sarebbe isolato e incapace di interferire con un'altra simulazione client tramite membri statici della classe ecc.

Il seguente è aiutato da un estratto dal libro di Joseph Albahari C# 4.0 In a Nutshell che vi consiglio vivamente di ottenere:

Un esempio di 40 simulazioni client simultanee potrebbe essere utile a voi:

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(); 
    } 
} 

Spero che questo aiuta.