Ci sono casi in cui è preferibile utilizzare un semplice oggetto Thread vecchio invece di uno dei costrutti più recenti?


112

Vedo molte persone nei post del blog e qui su SO che evitano o sconsigliano l'uso della Threadclasse nelle versioni recenti di C # (e intendo ovviamente 4.0+, con l'aggiunta di Task& friends). Anche prima, c'erano dibattiti sul fatto che la funzionalità di un semplice vecchio thread può essere sostituita in molti casi dalla ThreadPoolclasse.

Inoltre, altri meccanismi specializzati stanno ulteriormente rendendo la Threadclasse meno attraente, come Timers che sostituisce il brutto Thread+ Sleepcombo, mentre per le GUI abbiamo BackgroundWorker, ecc.

Tuttavia, Threadsembra rimanere un concetto molto familiare per alcune persone (me compreso), persone che, quando si confrontano con un compito che implica una sorta di esecuzione parallela, passano direttamente all'uso della buona vecchia Threadclasse. Ultimamente mi sono chiesto se è ora di modificare i miei modi.

Quindi la mia domanda è: ci sono casi in cui è necessario o utile utilizzare un semplice Threadoggetto vecchio invece di uno dei costrutti di cui sopra?


4
Non per nitpick (o forse ... :)), ma questo è .NET (cioè non limitato a C #).
MetalMikester

11
Finora il rappresentante medio per un utente che risponde a questa domanda è 64.5k.
Uno

1
@zzzzBov: stavo pensando di pubblicare la mia risposta ma ora non posso per paura di abbassare la media :)
Brian Gideon

@BrianGideon, non vedo motivo per cui questo dovrebbe impedire a te oa chiunque altro di rispondere a questa domanda.
zzzzBov

@ Brian Gideon: Con tutti i mezzi, per favore pubblica. :)
Tudor

Risposte:


107

La classe Thread non può essere resa obsoleta perché ovviamente è un file dettaglio di implementazione di tutti gli altri pattern che menzioni.

Ma non è proprio la tua domanda; la tua domanda è

ci sono casi in cui è necessario o utile utilizzare un semplice oggetto Thread vecchio invece di uno dei costrutti di cui sopra?

Sicuro. Proprio in quei casi in cui uno dei costrutti di livello superiore non soddisfa le tue esigenze.

Il mio consiglio è che se ti trovi in ​​una situazione in cui gli strumenti di astrazione superiore esistenti non soddisfano le tue esigenze e desideri implementare una soluzione utilizzando i thread, dovresti identificare l'astrazione mancante di cui hai veramente bisogno e quindi implementare tale astrazione utilizzando i thread e quindi utilizzare l'astrazione .


33
Tutti buoni consigli. Ma hai un esempio in cui un semplice oggetto thread vecchio potrebbe essere preferibile a uno dei costrutti più recenti?
Robert Harvey

Il debug di più thread con problemi di temporizzazione per caricare un po 'di codice può essere fatto molto più facilmente, a volte, utilizzando altri costrutti di livello "superiore". E hai comunque bisogno delle conoscenze di base per lavorare e usare i thread.
CodingBarfield

Grazie per la risposta, Eric. È davvero facile dimenticare, sotto il recente bombardamento sulle nuove funzionalità di threading, che a volte il vecchio thread è ancora necessario. Beh, immagino che la conoscenza del bare metal sia utile comunque.
Tudor

15
@RobertHarvey: certo. Ad esempio, se hai una classica situazione "produttore / consumatore", in cui vuoi avere un thread dedicato a portare cose fuori da una coda di lavoro e un altro thread dedicato a mettere cose in una coda di lavoro. Entrambi si ripetono per sempre; non si associano bene alle attività che svolgi per un po 'e poi ottengono un risultato. Non è necessario un pool di thread perché ci sono solo due thread. E puoi essere intelligente con il tuo uso di blocchi e segnali per assicurarti che la coda sia tenuta al sicuro senza bloccare l'altro thread per molto tempo.
Eric Lippert

@ Eric: questa astrazione non è già offerta da TPL Dataflow ?
Allon Guralnek

52

I thread sono un elemento fondamentale per alcune cose (vale a dire parallelismo e asincronia) e quindi non dovrebbero essere portati via. Tuttavia , per la maggior parte delle persone e per la maggior parte dei casi d'uso ci sono cose più appropriate da usare che hai menzionato, come i pool di thread (che forniscono un bel modo di gestire molti piccoli lavori in parallelo senza sovraccaricare la macchina generando 2000 thread contemporaneamente),BackgroundWorker ( che racchiude eventi utili per un singolo lavoro di breve durata).

Ma solo perché in molti casi quelli sono più appropriati in quanto proteggono il programmatore dal reinventare inutilmente la ruota, facendo errori stupidi e simili, ciò non significa che la classe Thread sia obsoleta. È ancora usato dalle astrazioni sopra citate e ne avresti ancora bisogno se hai bisogno di un controllo granulare sui thread che non è coperto dalle classi più speciali.

Allo stesso modo, .NETnon vieta l'uso di array, nonostante List<T>sia più adatto per molti casi in cui le persone usano gli array. Semplicemente perché potresti ancora voler costruire cose che non sono coperte dalla libreria standard.


6
Solo perché una trasmissione automatica funziona il 99% delle volte, non significa che non ci sia alcun valore nelle trasmissioni manuali, anche ignorando le preferenze del conducente.
Guvante

4
Non ho molta familiarità con gli idiomi di guida dato che sono un ciclista e un utente dei trasporti pubblici. Ma una trasmissione manuale non è al centro di una trasmissione automatica - non è una mano meccanica che muove un bastone. Non è davvero un'astrazione sull'altra, per quanto posso vedere.
Joey

2
Potresti sostituire la parola "thread" con "codice non gestito" nel secondo paragrafo e continuerà a suonare vero
Conrad Frix

1
@ Joey: Oh, pensavo intendessi il pezzo di obsolescenza, il valore di avere un controllo a grana fine, a scapito dell'usabilità, anche se la maggior parte non ne avrà mai bisogno. A proposito, tecnicamente la parte interessante di un cambio manuale è comunque la frizione.
Guvante

3
Se volete davvero seguire la metafora della trasmissione, la maggior parte delle trasmissioni sequenziali (come la trasmissione SMG di BMW) sono , in effetti, trasmissioni manuali con frizione e selettore di marcia azionati da computer. Non sono sistemi di ingranaggi planetari come gli automatismi convenzionali.
Adam Robinson

13

Taske Threadsono astrazioni diverse. Se vuoi modellare un thread, la Threadclasse è comunque la scelta più appropriata. Ad esempio, se hai bisogno di interagire con il thread corrente, non vedo tipi migliori per questo.

Tuttavia, come hai sottolineato, .NET ha aggiunto diverse astrazioni dedicate che sono preferibili Threadin molti casi.


9

La Threadclasse non è obsoleta, è ancora utile in circostanze speciali.

Dove lavoro abbiamo scritto un "processore in background" come parte di un sistema di gestione dei contenuti: un servizio Windows che monitora directory, indirizzi e-mail e feed RSS, e ogni volta che si presenta qualcosa di nuovo esegue un'attività su di esso, in genere per importare il dati.

I tentativi di utilizzare il pool di thread per questo non hanno funzionato: tenta di eseguire troppe cose contemporaneamente e di cestinare i dischi, quindi abbiamo implementato il nostro sistema di polling ed esecuzione utilizzando direttamente la Threadclasse.


Non ho idea se usare Thread direttamente qui sia la soluzione giusta qui, ma +1 per dare effettivamente un esempio in cui era richiesto andare a Thread.
Oddthinking

6

Le nuove opzioni rendono meno frequente l' uso diretto e la gestione dei thread (costosi).

persone che, quando si confrontano con un'attività che implica una sorta di esecuzione parallela, passano direttamente all'uso della buona vecchia classe Thread.

Che è un modo molto costoso e relativamente complesso di fare cose in parallelo.

Nota che la spesa conta di più: non puoi usare un thread completo per fare un piccolo lavoro, sarebbe controproducente. Il ThreadPool combatte i costi, la classe Task le complessità (eccezioni, attesa e annullamento).


5

Per rispondere alla domanda " ci sono casi in cui è necessario o utile utilizzare un semplice oggetto Thread vecchio ", direi che un vecchio Thread semplice è utile (ma non necessario) quando hai un processo a lungo in esecuzione che non hai vinto " Non interagire mai con da un thread diverso.

Ad esempio, se stai scrivendo un'applicazione che si iscrive per ricevere messaggi da una sorta di coda di messaggi e la tua applicazione farà di più che elaborare quei messaggi, allora sarebbe utile usare un thread perché il thread sarà autonomo (cioè non lo stai aspettando per essere fatto) e non è di breve durata. L'uso della classe ThreadPool è più per mettere in coda un mucchio di elementi di lavoro di breve durata e consentire alla classe ThreadPool di gestire in modo efficiente l'elaborazione di ciascuno di essi quando è disponibile un nuovo thread. Le attività possono essere utilizzate dove useresti Thread direttamente, ma nello scenario sopra non penso che ti comprerebbero molto. Ti aiutano a interagire con il thread più facilmente (cosa che lo scenario precedente non fa


Sono d'accordo che la maggior parte delle nuove cose interessanti del parallelismo sono progettate attorno a piccole attività a grana fine a breve termine e che i thread a esecuzione lunga dovrebbero ancora essere implementati con System.IO.Threading.Thread.
Ben Voigt

4

Probabilmente non è la risposta che ti aspettavi, ma uso Thread tutto il tempo durante la codifica con .NET Micro Framework. MF è abbastanza ridotto e non include astrazioni di livello superiore e la classe Thread è super flessibile quando è necessario ottenere l'ultimo bit di prestazioni da una CPU a basso MHz.


Ehi, questo è davvero un buon esempio! Grazie. Non ho pensato a framework che non supportano le nuove funzionalità.
Tudor

3

È possibile confrontare la classe Thread con ADO.NET. Non è lo strumento consigliato per portare a termine il lavoro, ma non è obsoleto. Altri strumenti si basano su di esso per facilitare il lavoro.

Non è sbagliato usare la classe Thread rispetto ad altre cose, specialmente se queste cose non forniscono una funzionalità di cui hai bisogno.


3

Non è decisamente obsoleto.

Il problema con le app multithread è che sono molto difficili da ottenere correttamente (il comportamento, l'input, l'output e anche lo stato interno spesso indeterministici sono importanti), quindi un programmatore dovrebbe spingere quanto più lavoro possibile a framework / strumenti. Estratto via. Ma il nemico mortale dell'astrazione è la performance.

Quindi la mia domanda è: ci sono casi in cui è necessario o utile utilizzare un semplice oggetto Thread vecchio invece di uno dei costrutti sopra?

Andrei con thread e blocchi solo se ci saranno seri problemi di prestazioni, obiettivi ad alte prestazioni.


3

Ho sempre usato la classe Thread quando ho bisogno di tenere il conto e controllare i thread che ho creato. Mi rendo conto che potrei usare il threadpool per contenere tutto il mio lavoro eccezionale, ma non ho mai trovato un buon modo per tenere traccia di quanto lavoro è attualmente in corso o qual è lo stato.

Invece, creo una raccolta e inserisco i thread al loro interno dopo averli ruotati: l'ultima cosa che un thread fa è rimuovere se stesso dalla raccolta. In questo modo, posso sempre dire quanti thread sono in esecuzione e posso usare la raccolta per chiedere a ciascuno cosa sta facendo. Se c'è un caso in cui ho bisogno di ucciderli tutti, normalmente dovresti impostare una sorta di flag "Abort" nella tua applicazione, aspettare che ogni thread se ne accorga da solo e si interrompa automaticamente - nel mio caso, io può camminare nella raccolta ed emettere un Thread.Abort a ciascuno a turno.

In tal caso, non ho trovato un modo migliore che lavorare direttamente con la classe Thread. Come ha detto Eric Lippert, le altre sono solo astrazioni di livello superiore ed è appropriato lavorare con le classi di livello inferiore quando le implementazioni di alto livello disponibili non soddisfano le tue necessità. Proprio come a volte è necessario eseguire chiamate API Win32 quando .NET non soddisfa esattamente le proprie esigenze, ci saranno sempre casi in cui la classe Thread è la scelta migliore nonostante i recenti "progressi".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.