Attività possono essere utilizzati per rappresentare le operazioni che si svolgono su più thread, ma non hanno a. Uno può scrivere applicazioni TPL complesse che vengono eseguite solo in un singolo thread. Quando si dispone di un'attività che, ad esempio, rappresenta una richiesta di rete per alcuni dati, tale attività è non andando a creare ulteriori thread per raggiungere tale obiettivo. Un tale programma è (si spera) asincrono, ma non necessariamente in versione mutlithread.
Il parallelismo fa più di una cosa allo stesso tempo. Questo può o non può essere il risultato di più thread.
Facciamo un'analogia qui.
Ecco come Bob prepara la cena:
- riempie una pentola di acqua e bolle di esso.
- Quindi mette la pasta nell'acqua.
- Vuota la pasta quando è finita.
- Prepara gli ingredienti per la sua salsa.
- Lui mette tutti gli ingredienti per la sua salsa in una casseruola.
- Lui cucina la sua salsa.
- Si mette la salsa sulla sua pasta.
- Lui mangia la cena.
Bob ha cucinato interamente in modo sincrono senza il multithreading, l'asincronia o il parallelismo quando cucinava la sua cena.
Ecco come Jane cuoce la cena:
- Si riempie una pentola di acqua e comincia a bollire.
- Lei prepara gli ingredienti per la sua salsa.
- Mette la pasta nell'acqua bollente.
- Lei mette gli ingredienti nella casseruola.
- Vuota la sua pasta.
- Lei mette la salsa sulla sua pasta.
- Lei mangia la sua cena.
Jane leveraged cottura asincrono (senza multithreading) per ottenere il parallelismo durante la cottura la sua cena.
Ecco come Servy prepara la cena:
- dice Bob a bollire una pentola d'acqua, mettere la pasta quando è pronto, e servire la pasta.
- Dice a Jane di preparare gli ingredienti per la salsa, di cucinarla e di servirla sulla pasta quando è pronta.
- Aspetta che Bob e Jane finiscano.
- Lui mangia la sua cena.
SERVY Più thread leveraged (lavoratori), che ciascuno ha fatto individualmente il loro lavoro in modo sincrono, ma chi ha lavorato in modo asincrono rispetto gli uni agli altri per ottenere il parallelismo.
Naturalmente ciò diventa tanto più interessante se consideriamo, ad esempio, se la nostra stufa ha due bruciatori o solo uno. Se la nostra stufa ha due fornelli, i nostri due fili, Bob e Jane, sono entrambi in grado di fare il loro lavoro senza intromettersi a vicenda. Potrebbero sbattere un po 'le spalle, o ogni tentativo di afferrare qualcosa dallo stesso cabinet di tanto in tanto, quindi verranno rallentati di uno bit, ma non di molto. Se ognuno di loro ha bisogno di condividere un singolo fornello, allora non sarà in grado di fare molto di più quando l'altro sta facendo il lavoro. In tal caso, il lavoro non verrà eseguito più rapidamente di una sola persona che esegue la cottura completamente in modo sincrono, come fa Bob quando è da solo. In questo caso stiamo cucinando con più thread, ma la nostra cottura non è parallelizzata allo. Non tutto il lavoro con multithreading è in realtà il lavoro parallelo. Questo è ciò che accade quando si eseguono più thread su una macchina con una CPU. In realtà non si ottiene un lavoro più veloce del semplice uso di un thread, perché ogni thread sta lavorando a turno. (Ciò non significa che i programmi multithread siano inutili su CPU di un core, non lo sono, è solo che la ragione per usarli non è migliorare la velocità.)
Possiamo anche considerare come questi cuochi avrebbero fatto il loro lavoro utilizzando la libreria Task Parallel, per vedere che cosa utilizza del TPL corrispondere a ciascuno di questi tipi di cuochi:
Quindi, prima abbiamo bob , solo la scrittura normale codice non-TPL e facendo tutto in modo sincrono:
public class Bob : ICook
{
public IMeal Cook()
{
Pasta pasta = PastaCookingOperations.MakePasta();
Sauce sauce = PastaCookingOperations.MakeSauce();
return PastaCookingOperations.Combine(pasta, sauce);
}
}
poi abbiamo Jane, che inizia due diverse operazioni asincrone, quindi attende per tutti e due dopo l'inizio ciascuno di loro per calcolare il suo risultato.
public class Jane : ICook
{
public IMeal Cook()
{
Task<Pasta> pastaTask = PastaCookingOperations.MakePastaAsync();
Task<Sauce> sauceTask = PastaCookingOperations.MakeSauceAsync();
return PastaCookingOperations.Combine(pastaTask.Result, sauceTask.Result);
}
}
Come promemoria qui, Jane sta usando il TPL, e sta facendo molto del suo lavoro in parallelo, ma è solo con un singolo thread a fare il suo lavoro.
Poi abbiamo Servy, che usa Task.Run
per creare un'attività che rappresenta facendo un lavoro in un altro thread. Inizia due diversi lavoratori, li fa lavorare entrambi in modo sincrono, quindi aspetta che entrambi i lavoratori finiscano.
public class Servy : ICook
{
public IMeal Cook()
{
var bobsWork = Task.Run(() => PastaCookingOperations.MakePasta());
var janesWork = Task.Run(() => PastaCookingOperations.MakeSauce());
return PastaCookingOperations.Combine(bobsWork.Result, janesWork.Result);
}
}
semplicemente "fantastico". Grande analogia. Grazie :) –
+1 La cena di Servy era deliziosa, Diamo un'occhiata anche a [Eric Lippert's Breakfast] (http://msdn.microsoft.com/en-us/magazine/hh456401.aspx) :) –
Ben scritto! L'analogia con il bruciatore è brillante. –