2009-10-24 3 views
7

Sto utilizzando metodi asincroni in alcuni dei miei progetti e mi piace perché consente alla mia applicazione di essere molto più scalabile. Tuttavia, mi chiedo come i metodi asincroni funzionano davvero in background? In che modo .NET (o Windows?) Sa che una chiamata è stata completata? A seconda del numero di chiamate asincrone che ho fatto, posso vedere che vengono creati nuovi thread (non sempre però ...). Perché?Come funzionano i metodi asincroni in C#?

Inoltre, vorrei monitorare quanto tempo occorre per completare una richiesta. Per testare il concetto, ho scritto il seguente codice che chiama in modo asincrono un servizio Web e immediatamente dopo avvia un cronometro.

for (int i = 0; i < 10000; i++) 
{ 
    myWebService.BeginMyMethod(new MyRequest(), result, new AsyncCallback(callback), i); 
    stopWatches[i].Start(); 
} 
// Call back stop the stopwatch after calling EndMyMethod 

Questo non funziona in quanto tutte le richieste (10000) hanno lo stesso inizio ora e la durata salirà linearmente (chiamata 0 = durata 1, chiamare il numero 1 = durata 2, ecc). Come posso monitorare la durata reale di una chiamata con metodo asincrono (dal momento in cui la richiesta viene effettivamente eseguita fino alla fine)?


UPDATE: Esiste un metodo asincrono bloccare il flusso? Comprendo che utilizza .NET ThreadPool ma come un IAsyncResult sa che una chiamata è completata ed è ora di chiamare il metodo CallBack?

+0

1. ThreadPool, 2. Vedere i dettagli su come funziona il ThreadPool ma non penso che ciò che si desidera sia possibile in un modo generico. http://msdn.microsoft.com/en-us/library/ms973903.aspx –

risposta

3

Il codice è la ferrovia e il filo è il treno. Mentre il treno va in ferrovia, esegue il codice.

BeginMyMethod viene eseguito dalla filettatura principale. Se si guarda all'interno di BeginMyMethod, viene semplicemente aggiunto un delegato di MyMethod alla coda di ThreadPool.L'attuale MyMethod viene eseguito da uno dei treni della piscina del treno. La routine di completamento che viene chiamata quando MyMethod viene eseguita viene eseguita dallo stesso thread che ha eseguito il MyMethod, non dal thread principale che esegue il resto del codice. Mentre un thread pool di thread è occupato nell'esecuzione di MyMethod, il thread principale può utilizzare un'altra parte del sistema ferroviario (eseguire un altro codice) o semplicemente dormire, in attesa fino a quando non viene acceso il semaforo.

Quindi non esiste una cosa come IAsyncResult "sapere" quando chiamare la routine di completamento, invece, la routine di completamento è semplicemente un delegato chiamato dal thread del thread thread subito dopo aver eseguito l'esecuzione di MyMethod.

Spero non ti dispiaccia l'analogia del treno un po 'infantile, so che mi ha aiutato più di una volta a spiegare il multithreading alle persone.

+0

Mi piace l'analogia del treno! Grazie! :) – Martin

1

Il punto cruciale è che chiamare Begin accoda una richiesta per eseguire il metodo. Il metodo viene effettivamente eseguito su ThreadPool, che è un insieme di thread di lavoro fornito dal runtime.

Il threadpool è un set fisso di thread per eseguire operazioni asincrone durante la messa in coda. Ciò spiega perché i tempi di esecuzione sono più lunghi e più lunghi: i tuoi metodi possono essere eseguiti approssimativamente nello stesso tempo, ma non si avviano finché non sono stati eseguiti tutti i metodi precedenti nella coda.

Per monitorare il tempo necessario per eseguire effettivamente il metodo asincrono, è necessario avviare e arrestare il timer all'inizio e alla fine del metodo.

Ecco i documenti per la classe ThreadPool e un articolo su async methods che fanno un lavoro migliore per spiegare cosa sta succedendo.

1

I metodi asincroni funzionano utilizzando .NET ThreadPool. Spingeranno il lavoro su un thread ThreadPool (potenzialmente creando uno se necessario, ma di solito solo riutilizzandolo) per lavorare in background.

Nel tuo caso, puoi fare quello che stai facendo, tuttavia, renditi conto che il ThreadPool ha un numero limitato di thread con cui funzionerà. Distribuirai il tuo lavoro sui thread in background e il primo verrà eseguito immediatamente, ma dopo un po 'si accoderanno e non funzioneranno finché le "attività" non verranno eseguite completamente. Questo darà l'aspetto dei fili che richiedono sempre più tempo.

Tuttavia, i criteri del cronometro sono in qualche modo difettosi. È necessario misurare il tempo totale necessario per completare N task, non N volte per completare un'attività. Questa sarà una metrica molto più utile.

0

È possibile che la maggior parte del tempo di esecuzione avvenga prima dello BeginMyMethod(). In quel caso la tua misura sarà troppo bassa. Infatti, a seconda dell'API, BeginMyMethod() può richiamare la richiamata prima di lasciare lo stack stesso. Spostare la chiamata a StopWatch.Start() dovrebbe quindi aiutare.