19

Recentemente, durante l'intervista, ho ricevuto questa domanda.L'uso della libreria Task (TPL) rende un'applicazione multithread?

D: Hai scritto applicazioni multithread?

A: Sì

Q: Ti va di spiegare di più?

A: Ho utilizzato Tasks (libreria Attività parallela) per eseguire alcune attività come waiting for some info from internet while loading UI. Ciò migliora la fruibilità dell'applicazione.

D: Ma hai appena utilizzato TPL significa che hai scritto un'applicazione multithreaded?

Me: (Non sai cosa say1)

Così, che cosa è esattamente un'applicazione multi-threaded? È diverso dall'uso di Tasks?

risposta

77

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:

  1. riempie una pentola di acqua e bolle di esso.
  2. Quindi mette la pasta nell'acqua.
  3. Vuota la pasta quando è finita.
  4. Prepara gli ingredienti per la sua salsa.
  5. Lui mette tutti gli ingredienti per la sua salsa in una casseruola.
  6. Lui cucina la sua salsa.
  7. Si mette la salsa sulla sua pasta.
  8. 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:

  1. Si riempie una pentola di acqua e comincia a bollire.
  2. Lei prepara gli ingredienti per la sua salsa.
  3. Mette la pasta nell'acqua bollente.
  4. Lei mette gli ingredienti nella casseruola.
  5. Vuota la sua pasta.
  6. Lei mette la salsa sulla sua pasta.
  7. 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:

  1. dice Bob a bollire una pentola d'acqua, mettere la pasta quando è pronto, e servire la pasta.
  2. Dice a Jane di preparare gli ingredienti per la salsa, di cucinarla e di servirla sulla pasta quando è pronta.
  3. Aspetta che Bob e Jane finiscano.
  4. 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); 
    } 
} 
+7

semplicemente "fantastico". Grande analogia. Grazie :) –

+3

+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) :) –

+3

Ben scritto! L'analogia con il bruciatore è brillante. –

2

Un Task è una promessa per il lavoro futuro per essere completato. Quando lo si utilizza, è possibile utilizzarlo per il lavoro I/O based, che non è richiedere l'utilizzo di più thread per l'esecuzione del codice. Un buon esempio è l'utilizzo della funzionalità di C# 5 di async/await con HttpClient che funziona con I/O basato su rete.

Tuttavia, si può approfittare del TPL per fare il lavoro multithread. Ad esempio, quando si utilizza Task.Run o Task.Factory.Startnew per iniziare una nuova attività, dietro le quinte del lavoro viene messo in coda per voi sul ThreadPool, che il TPL astrae per noi, che consente di utilizzare più thread.

Uno scenario comune per l'utilizzo di più thread è quando si hanno CPU bound lavoro che può essere fatto contemporaneamente (in parallelo). Lavorare con un'applicazione multithreaded ha una grande responsabilità.

Quindi vediamo che lavorare con lo TPL significa non utilizzare necasserly più thread, ma sicuramente puoi sfruttarlo per fare il multithreading.

2

D: Ma, appena si è utilizzato TPL significa che hai scritto un'applicazione multithread ?

intelligente domanda, Task! = Multi-threaded, Usare TaskCompletionSource è possibile creare un Task che può eseguire in unico filo (può essere solo thread UI) stesso.

Task è solo un'astrazione corso di un'operazione che può completare in futuro. Non significa che il codice è multi-thread. Solitamente Task implica il multi-threading, non necessariamente sempre.

e tenere a mente solo con la conoscenza di TPL non si può dire che si sa multi-threading. Ci sono molti concetti che devi coprire.

  • filettatura
  • sincronizzazione primitive
  • sicurezza filettatura
  • Asynchronous programmazione
  • programmazione parallela che è una parte di TPL.
  • e molto altro ancora ...

e, naturalmente, Task Parallel Library troppo.

Nota: questa non è la lista completa, questi sono solo dall'alto della mia testa.


risorse per imparare:

Per filettatura da solo, suggerisco http://www.albahari.com/threading/

Per video tutorial suggerisco Pluralsight. È pagato ma vale il costo.

Ultimo ma non meno importante: Sì, naturalmente, è Stackoverflow.

+0

@nowhewhomustnotbenamed, controlla anche il blog di Stephen Cleary (e anche il suo libro): http://blog.stephencleary.com. Inoltre, il [tag wiki "task-parallel-library'] (http://stackoverflow.com/tags/task-parallel-library/info) elenca alcuni e-book gratuiti. – Noseratio

+0

@sriram Sakthivel: Grazie mille, Sriram. Risorse molto interessanti. Grazie ancora :) –

1

miei 5 centesimi: non c'è bisogno di impegnarsi in modo esplicito le discussioni con Task.Run o Task.Factory.StartNew per rendere il vostro multithread applicazione TPL. Considerate questo:

async static Task TestAsync() 
{ 
    Func<Task> doAsync = async() => 
    { 
     await Task.Delay(1).ConfigureAwait(false); 
     Console.WriteLine(new { Thread.CurrentThread.ManagedThreadId }); 
    }; 

    var tasks = Enumerable.Range(0, 10).Select(i => doAsync()); 
    await Task.WhenAll(tasks); 
} 

// ... 
TestAsync().Wait(); 

Il codice dopo await all'interno doAsync viene eseguito contemporaneamente su diversi thread. In modo simile, la concorrenza può essere introdotta con l'API async socket, HttpClient, Stream.ReadAsync o qualsiasi altra cosa che utilizza il pool di thread (inclusi i thread di pool IOCP).

In senso figurato, ogni applicazione .NET è già multithread, poiché il Framework utilizza estesamente ThreadPool. Anche un'applicazione di console semplice mostrerà più di un thread per System.Diagnostics.Process.GetCurrentProcess().Threads.Count. Il tuo intervistatore dovrebbe averti chiesto se hai scritto simultaneo codice (o parallelo).