2012-12-18 12 views
6

Ho un'applicazione per Windows a cui voglio aggiungere la funzionalità TPL. Lo scenario è che ho una classe con un metodo Process che ha una serie di MailMessage s passati ad essa, così come la connessione IMAP corrente (usando AE.Net.Mail).Come si usa Task per eseguire molti lavori "Fire and Forget"?

Voglio spin off come tanti fili possibile di un metodo Execute in un'altra classe che prende un singolo MailMessage elemento, e il MailMessage al DB, e poi utilizza la connessione IMAP per eliminare il MailMessage dal server.

Non mi preoccupo troppo di tenere traccia dei processi - Ho a che fare con un gran numero di e-mail, e non mi preoccupo se ottengo degli errori nella scrittura nel DB o nella cancellazione. Ho solo bisogno dell'applicazione per superare un gran numero di MailMessage il più velocemente possibile.

Ho giocato con Task<MailMessage>.Factory.StartNew ma non so davvero cosa sto facendo. Non mi sembra di essere in grado di dare il via ... ecco cosa ho provato:

Task test = Task.Factory.StartNew(() => 
{ 
    foreach (var mailMessage in _mms) 
    { 
    new ProcessMessage().Execute(mailMessage, imapConn); 
    } 

}); 

Sono abbastanza sicuro che non avrei dovuto un ciclo nell'espressione Lamda, quando ho eseguito questo lo fa non sembra andare in ProcessMessage.Execute.

risposta

4

Non si dovrebbe assolutamente avere un ciclo nell'espressione di lamdba. Prova questo:

_mms.ForEach(mms => { 
    Task.Factory.StartNew(() => ProcessMessage().Execute(mailMessage, imapConn)) 
}); 

Se non siete preoccupati per tenere traccia dei risultati o qualsiasi cosa che non è necessario per salvare un'istanza di un compito così come prova Task = .... si può semplicemente il metodo kickstart esecuzione in un nuovo thread utilizzando Task.Factory.StartNew() In questo modo possiamo semplicemente avviare una nuova attività per ogni messaggio di posta che si desidera elaborare e lasciare che il pool di thread si prenda cura delle cose per noi.

Inoltre, Task<MailMessage>.Factory.StartNew verrebbe utilizzato per impostare una chiamata di metodo in un altro thread che restituisce un messaggio di posta, pertanto se si chiama un metodo di annullamento non è necessario eseguire questa operazione. La sintassi Task<object> fa sempre riferimento al tipo restituito del metodo che si sta avviando con una nuova attività.

+1

@JasonHiggins Ha suggerito un approccio che può essere facilmente reso più leggibile ed efficiente (utilizzando 'Parallel.ForEach()'). E ha usato un metodo ('ForEach()' invece di 'foreach') che a volte è disapprovato. Quindi, non andrei tanto lontano dicendo che è il prossimo Jon Skeet :-) – svick

+1

L'OP aveva domande specifiche riguardo al TPL ed è per questo che ho deciso di usare questo approccio nella mia risposta. Inoltre, per quanto riguarda foreach vs ForEach, è a mia conoscenza che il metodo di estensione di ForEach è disapprovato nei casi in cui vi sono potenziali effetti collaterali indesiderati e non penso che l'esecuzione di un metodo di annullamento utilizzando il TPL sarebbe un caso negativo da utilizzare questa versione Se ti senti più a tuo agio con il ciclo foreach è certamente facile tradurre questo codice per usarlo. Detto questo, certamente non mi paragonerei a Jon Skeet :) –

1

Questo dovrebbe fare ogni esecuzione in thread separato (in modo da sapere cosa si sta facendo ora :))

foreach (var mailMessage in _mms) 
    { 
    ThreadPool.QueueUserWorkItem(delegate 
    { 
     new ProcessMessage().Execute(mailMessage, imapConn); 
    }); 
    } 

o

foreach (var mailMessage in _mms) 
    { 
     new Thread(delegate() { new ProcessMessage().Execute(mailMessage, imapConn); }).Start(); 
    } 
+3

Perché suggeriresti di usare 'ThreadPool' o' Thread' quando puoi usare 'Task's? E creare una grande quantità di 'Thread's è una cattiva idea. – svick

+0

@svick puoi e ragazzi come voi smettere di dire che è una cattiva idea e dire il motivo per cui è una cattiva idea? E perché suggerisci di usare Google Task se ci sono thread? Pensi davvero che creare 1000 Task sia meglio di creare 1000 thread in coda? – VladL

+3

Assolutamente, 1000 'Task's è * molto * migliore di 1000' Thread's (non c'è niente in coda su di loro). Ogni 'Thread' consuma 1 MB di memoria (e alcune altre risorse), mentre 1000' Task's userà 'ThreadPool' per eseguire con un ingombro di memoria molto più piccolo. Pertanto, il tuo approccio sprecherà risorse in modo considerevole e può anche facilmente causare "OutOfMemoryException", se stai utilizzando un processo a 32 bit. – svick

4

In questo momento si sta eseguendo il ciclo foreach come un separato attività, ma probabilmente si desidera eseguire ciascuna iterazione come attività separata. Si dovrebbe provare il Parallel.ForEach:

 Parallel.ForEach(_mms, mailMessage => 
      { 
       new ProcessMessage().Execute(mailMessage, imapConn); 
      }); 

Questo eseguirà iterazioni in parallelo, che sembra essere quello che stai cercando di fare.

+0

Ci sono alcune imprecisioni nella risposta. 'Parallel.ForEach()' non esegue ogni iterazione come 'Task' separata, perché l'uso di un' Task' per più iterazioni ha un overhead minore. E se "esegui tutte le iterazioni in parallelo" intendi "esegui tutte le iterazioni contemporaneamente", allora anche questo è sbagliato. – svick

+0

@svick Non ricordo di aver affermato che Parallel.Foreach() esegue ogni iterazione in un'attività separata ... E probabilmente dovrei rimuovere la parola "tutto" per essere più esatti. –

+0

Il modo in cui hai scritto la tua risposta implica che: "probabilmente vorrai eseguire ogni iterazione come un'attività separata. Dovresti provare Parallel.ForEach [per farlo] " – svick

1

quando ho eseguito questo non sembra andare in ProcessMessage.Execute.

Quindi ci deve essere qualcos'altro che non funziona nel codice, il codice dovrebbe funzionare.E avere un loop nel lambda potrebbe essere del tutto appropriato se non vuoi (o non puoi) parallelizzare il tuo codice.

Se si desidera parallelizzarlo (che non è chiaro dalla domanda), è possibile utilizzare ad esempio Parallel.ForEach() che è ottimizzato esattamente per questo.