2012-05-10 6 views
12

I delegati sono alcuni degli oggetti che semplificano la filettatura in .NET reference. Possono essere utilizzati per invocare in modo asincrono un metodo. Quali altri oggetti esistono nel framework 4.5 (o precedenti) che rendono l'uso dei thread più facile o meno incline agli errori?Quali costrutti di livello superiore .NET 4.5 (o precedenti) rendono più semplice la filettatura?

Quali sono le altre astrazioni che semplificano la concorrenza e il multithreading?

Nota: questa domanda viene aggiornata this.

+4

I delegati e gli eventi non hanno nulla a che fare con il threading e non sono specifici per .NET 4.5. Per favore sii più chiaro, dato che non posso dire di cosa stai parlando. –

+0

@JohnSaunders I delegati asincroni rendono più semplice il threading; vedi http://msdn.microsoft.com/en-us/library/22t547yb.aspx – LamonteCristo

+0

Hai notato che l'uso asincrono dei delegati è stato in .NET dalla versione 1.0? –

risposta

21

Tendo a rispondere a molte domande relative al multithreading e spesso vedo la stessa domanda di base posta in vari modi. Presenterò i problemi più comuni come li ho visti nel corso degli anni e spiegherò come le nuove tecnologie hanno reso più facile la risoluzione di questi problemi.

Chiusura sopra la variabile del ciclo

Questo non è un problema specifico di threading, ma l'uso di infilare ingrandisce definitivamente il problema. C# 5.0 risolve questo problema per il ciclo foreach creando una nuova variabile per ogni iterazione. Non sarà più necessario creare una variabile speciale per le chiusure di espressioni lambda. Sfortunatamente, il ciclo for dovrà ancora essere gestito con una variabile di cattura speciale.

In attesa di compiti asincroni per completare

NET 4.0 ha introdotto la classe CountdownEvent che incapsula un sacco della logica necessaria per attendere il completamento di molte attività. La maggior parte degli sviluppatori junior utilizzava le chiamate Thread.Join o una singola chiamata WaitHandle.WaitAll. Entrambi hanno problemi di scalabilità. Il vecchio schema prevedeva l'utilizzo di un singolo ManualResetEvent e lo segnalava quando un contatore raggiungeva lo zero. Il contatore è stato aggiornato utilizzando la classe Interlocked. CountdownEvent rende questo schema molto più semplice. Ricordatevi solo di trattare il vostro principale come un lavoratore per evitare quella sottile condizione di razza che può verificarsi se un lavoratore finisce prima che tutti i lavoratori siano stati accodati.

.NET 4.0 ha introdotto anche la classe Task che può avere attività figlio incatenate da esso tramite TaskCreationOptions.AttachedToParent. Se chiami Task.Wait su un genitore, attenderà il completamento di tutte le attività figlio.

produttore-consumatore

.NET 4.0 ha introdotto la classe BlockingCollection che agisce come una coda normale eccetto che può bloccare quando la raccolta è vuota. È possibile mettere in coda un oggetto chiamando Add e deselezionare un oggetto chiamando Take. Take blocchi finché un articolo non è disponibile. Ciò semplifica considerevolmente la logica produttore-consumatore. Prima era il caso che gli sviluppatori stessero cercando di scrivere la propria classe di code di blocco. Ma se non sai cosa stai facendo, puoi davvero rovinare tutto ... male. In effetti, per un periodo di tempo prolungato, Microsoft ha avuto un esempio di coda di blocco nella documentazione MSDN, anch'essa gravemente danneggiata. Per fortuna, da allora è stato rimosso.

Aggiornamento UI con il progresso thread di lavoro

L'introduzione di BackgroundWorker fatto scorporo attività in background da un'applicazione WinForm molto più facile per gli sviluppatori alle prime armi. Il vantaggio principale è che è possibile chiamare ReportProgress dal gestore di eventi DoWork ei gestori di eventi ProgressChanged verranno automaticamente sottoposti a marshalling sul thread dell'interfaccia utente. Ovviamente, chiunque tenga traccia delle mie risposte su SO sa come mi sento riguardo le operazioni di marshalling (via Invoke o simili) come soluzione per aggiornare l'interfaccia utente con semplici informazioni sull'avanzamento. Ci strappo tutto il tempo perché è generalmente un approccio terribile. BackgroundWorker impone ancora lo sviluppatore in un modello push (tramite operazioni di marshalling in background), ma almeno lo fa dietro le quinte.

L'inelegance di Invoke

Tutti sappiamo che un elemento dell'interfaccia utente può accedere solo dal thread UI. Ciò significava in genere che uno sviluppatore doveva utilizzare operazioni di marshalling tramite ISynchronizeInvoke, DispatcherObject o SynchronizationContext per trasferire il controllo sul thread dell'interfaccia utente. Ma ammettiamolo. Queste operazioni di marshalling sembrano brutte. Task.ContinueWith ha reso questo un po 'più elegante, ma la vera gloria va a await come parte del nuovo modello di programmazione asincrona del C# 5. await può essere utilizzato per attendere il completamento di Task in modo tale che il controllo di flusso venga temporaneamente interrotto mentre l'attività è in esecuzione e quindi restituita proprio nel punto corretto nel contesto di sincronizzazione. Non c'è niente di più elegante e soddisfacente dell'uso di await in sostituzione di tutte quelle chiamate Invoke.

Programmazione parallela

vedo spesso domande chiedendo come le cose possono accadere in parallelo. Il vecchio modo era creare pochi thread o usare ThreadPool. .NET 4.0 ha utilizzato TPL e PLINQ. La classe Parallel è un ottimo modo per ottenere le iterazioni di un ciclo in parallelo. E il numero AsParallel di PLINQ è un altro lato della stessa medaglia per il vecchio LINQ semplice. Queste nuove funzionalità TPL semplificano notevolmente questa categoria di programmazione multithread.

.NET 4.5 introduce la libreria TPL Data Flow. È destinato a rendere elegante un problema di programmazione parallela altrimenti complesso. Estrae le classi in blocchi. Possono essere blocchi di destinazione o blocchi di origine. I dati possono fluire da un blocco all'altro. Ci sono molti blocchi diversi tra cui BufferBlock<T>, BroadcastBlock<T>, ActionBlock<T>, ecc. Che fanno cose diverse. E, naturalmente, l'intera libreria sarà ottimizzata per l'utilizzo con le nuove parole chiave async e await. È un nuovo ed entusiasmante insieme di lezioni che a mio avviso prenderanno lentamente piede.

cessazione Graceful

Come si ottiene un filo di smettere? Vedo molto questa domanda. Il modo più semplice è chiamare Thread.Abort, ma sappiamo tutti i pericoli di farlo ... Spero. Ci sono molti modi per farlo in sicurezza. .NET 4.0 ha introdotto un concetto più unificato chiamato cancellazione via CancellationToken e CancellationTokenSource. Le attività in background possono eseguire il sondaggio IsCancellationRequested o semplicemente chiamare ThrowIfCancellationRequested in punti sicuri per interrompere con garbo qualunque lavoro stessero facendo. Altre discussioni possono chiamare Cancel per richiedere la cancellazione.

+0

Wow, questo è proprio quello di cui avevo bisogno! Stavo guardando dappertutto per una visione di 1.000 piedi di cose e questo è quanto. Le persone troppo brutte hanno chiuso questa domanda; L'ho nominato per la riapertura. – LamonteCristo

+0

In Reactive Extensions (Rx) e StreamInsight dovrebbero essere inclusi in questo elenco? – LamonteCristo

+0

@ makerofthings7: Probabilmente ... quando avrò tempo lo farò. –

1

Il Task e Task<T>, ma sono stati qui dal .NET 4. async non funziona necessariamente con fili, vedere Jon's video from Øredev per una buona spiegazione.

2

Senza dubbio, fare i conti con la nuova libreria Tpl DataFlow (inclusa in .net 4.5) vi darà il maggiore impulso in termini di sviluppo simultaneo.

Se si tratta di app molto contemporanee, passare un giorno o due a familiarizzare con DataFlow. È seriamente buono.

7

Bene vediamo qui:

  1. La classe ThreadPool - un pò vecchio, ma ancora affidabile per un semplice modello produttore-consumatore.
  2. BackgoundWorker (.NET 2.0+) - un altro costrutto di vecchia scuola, che fornisce funzionalità utili per l'esecuzione di attività in background in applicazioni GUI.
  3. Timer s - utile per l'esecuzione di codice a intervalli specificati utilizzando una thread in background.
  4. La classe Task (.NET 4.0+) - le astrazioni di threading eseguite nel pool di thread sottostante e forniscono molte funzioni utili come il marshalling e la pianificazione delle eccezioni. Utile per il cosiddetto schema del "parallelismo delle attività".
  5. Parallel.For, Parallel.ForEach (.NET 4.0+) - ideale per eseguire la stessa operazione su un insieme di dati in parallelo. Utile per il cosiddetto modello di "parallelismo dei dati".
  6. Parallel.Invoke (.NET 4.0+) - un'ulteriore astrazione su Task s. Semplicemente spara diversi pezzi di codice (metodi, lambda) in parallelo.
  7. Collezioni simultanee (.NET 4.0+) - tutto ciò che serve per passare o condividere i dati tra i thread in modo efficiente e sicuro.
Problemi correlati