Cosa costituisce un uso corretto dei thread nella programmazione?


13

Sono stanco di sentire che le persone raccomandano di usare solo un thread per processore, mentre molti programmi ne usano fino a 100 per processo! prendiamo ad esempio alcuni programmi comuni

vb.net ide uses about 25 thread when not debugging
System uses about 100
chrome uses about 19
Avira uses more than about 50

Ogni volta che invio una domanda relativa al thread, mi viene quasi sempre in mente che non dovrei usare più di un thread per processore e tutti i programmi che menziono sopra stanno rovinando il mio sistema con un singolo processore.


7
Tale raccomandazione è di ampio respiro. Il limite di un thread per processore è appropriato solo per le applicazioni legate al calcolo. La maggior parte dei programmi sono associati a IO, sia che si tratti di traffico di rete, accesso al disco o persino RAM. Ecco perché i server Web, i database ecc. Dispongono di pool di thread con molti più thread rispetto ai core del processore.
Kilian Foth,

2
"Mi viene quasi sempre in mente che non dovrei usare più di un thread per processore"? Puoi pubblicare link o esempi? Quasi ogni volta?
S.Lott

2
"... le persone raccomandano di usare solo un thread per processo." Chi e 'questa gente? La programmazione è progredita significativamente dai tempi bui.
Rein Henrichs,

2
Non dovresti avere più di un thread dell'interfaccia utente per processo.
SLaks,

3
@Billy ONeal, la tua modifica ha reso la domanda insignificante
SK-logic,

Risposte:


22

dovresti usare solo un thread per processore,

Forse in HPC dove vuoi la massima efficienza - ma per il resto la cosa più stupida che abbia sentito oggi!

È necessario utilizzare il numero di thread appropriati per la progettazione del programma e garantire comunque prestazioni accettabili.

Per un web server potrebbe essere ragionevole lanciare un thread per ogni connessione in entrata (anche se ci sono modi migliori per server molto caricati).

Per un ide ogni strumento in esecuzione nel proprio thread non è irragionevole. Sospetto che molti dei thread segnalati per l'IDE .Net siano cose come la registrazione e le attività di I / O avviate nei propri thread in modo che possano continuare a essere sbloccate.


9
Ora mi stai chiedendo quale sia la cosa più stupida che tu abbia mai sentito!
Michael K,

3
@Michael - Ho insegnato agli studenti e ho lavorato a contratti di difesa - non crederesti alle cose più stupide che abbia mai sentito!
Martin Beckett,

1
Li abbiamo visti su TheDailyWTF.com?
FrustratedWithFormsDesigner,

non riesco davvero a trovarli ora, ma guarda questo link social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/…
Smith,

2
Hanno al massimo un thread associato alla CPU per processore assegnato all'applicazione. I thread associati a IO non sono un grosso problema (a parte la memoria che consumano) ed è importante ricordare che le app possono essere limitate a utilizzare solo un sottoinsieme delle CPU del sistema; dopo tutto, è (di solito) il computer dell'utente / amministratore e non quello del programmatore.
Donal Fellows,

2

Il consiglio one-thread-per-core si applica quando lo scopo è la velocità attraverso l'esecuzione parallela.

Un motivo completamente diverso e ugualmente valido è la semplicità del codice quando deve rispondere a eventi imprevedibili. Quindi, se un programma deve ascoltare su 100 socket e sembra prestare la massima attenzione a ciascuno, è un uso perfetto per il threading. Un altro esempio è un'interfaccia utente, in cui un thread gestisce gli eventi dell'interfaccia utente, mentre un altro esegue l'elaborazione in background.


1
L'elaborazione IO-bound può essere eseguita come un thread per origine evento oppure è possibile eseguire il multiplexing di più origini evento su un singolo thread. Il codice multiplexato è in genere sia più complesso che più efficiente.
Donal Fellows,

2

Volete un thread per ogni calcolo che può procedere a velocità diverse rispetto ad altri calcoli.

Per il calcolo parallelo associato alla CPU, che viene fornito in grandi blocchi di lavoro, in genere si desidera un thread per CPU, perché una volta che sono tutti occupati, più thread non aiutano e creano semplicemente l'utilità di pianificazione. Se i blocchi di lavoro hanno dimensioni irregolari nel tempo o vengono generati dinamicamente in fase di esecuzione (spesso si verifica quando si hanno strutture di dati complesse di grandi dimensioni da elaborare), è possibile collegare tali blocchi a molti thread, quindi uno scheduler ha sempre impostare per scegliere tra quando completare un blocco di lavoro, per tenere occupate tutte le CPU.

Per il calcolo associato I / O, generalmente si desidera un thread per ciascun "canale" I / O indipendente poiché comunicano a velocità diverse e i thread bloccati sul canale non impediscono ad altri thread di avanzare.


Basta essere consapevoli del fatto che questo stile di threading può portare ad alcuni programmi stranamente progettati. Ho visto un programma a 4 thread che aveva un thread per leggere i record da una tabella DB, un thread per scrivere i record trasformati in un socket, un thread per leggere le risposte a quelle scritture di socket (che sono tornate fuori servizio e in modo asincrono) e un thread per modificare il record DB originale con la risposta. Ne conseguirono condizioni di errore non intuitive.
Bruce Ediger,

Una vista è che questo stile produce programmi strani. Un altro punto di vista è questo lo stile naturale che i programmi avrebbero dovuto avere. Non so delle condizioni di errore "non intuitivo"; se si verificano molte cose e una di esse presenta un errore, assicurarsi che sia propagato correttamente attraverso i calcoli asincroni è un problema per molti linguaggi [stupidamente, le eccezioni Java non sono definite ai limiti del thread], ma non lo è un problema con lo stile del programma. (La nostra lingua di programmazione PARLANSE [vedi la mia biografia] gestisce in modo pulito le eccezioni oltre i limiti del thread, quindi è possibile farlo correttamente.).
Ira Baxter,

1

La regola empirica per i thread è che si desidera almeno un thread di lavoro "attivo" (in grado di eseguire immediatamente i comandi alla volta della CPU) per ogni "unità di esecuzione" disponibile sul computer. Una "unità di esecuzione" è un processore di istruzioni logico, quindi un server hyperthreaded Xeon quad-chip e quad-core avrebbe 32 EU (4 chip, 4 core per chip, ognuno hyperthreaded). Il tuo Core i7 medio avrebbe 8.

Un thread per UE è il massimo utilizzo della potenza della CPU, a condizione che i thread siano sempre in esecuzione; questo non è quasi mai il caso, poiché i thread hanno bisogno dell'accesso alla memoria non cache, al disco rigido, alle porte di rete, ecc. che devono attendere e che non richiedono l'attenzione della CPU attiva per essere eseguiti. È quindi possibile aumentare ulteriormente l'efficienza complessiva con più thread in coda e raramente disponibili. Questo ha un costo; quando una CPU scambia un thread, deve memorizzare nella cache i registri del thread, il puntatore di esecuzione e altre informazioni sullo stato normalmente mantenute nei meccanismi più interni di un UE e molto rapidamente accessibili, consentendo ad altri UE in quel chip CPU di raccoglierlo. Richiede inoltre thread nel sistema operativo per decidere a quale thread deve essere attivato. Infine, quando un'UE cambia thread, perde i guadagni in termini di prestazioni della pipeline utilizzata dalla maggior parte delle architetture di processori; deve svuotare la tubazione prima di cambiare thread. Ma, poiché tutto ciò richiede ancora molto meno tempo in media rispetto alla semplice attesa del ritorno del disco rigido o della RAM con le informazioni, ne vale la pena.

Tuttavia, in generale, una volta superato il doppio del numero di thread "attivi" rispetto agli europei, il sistema operativo inizia a dedicare più tempo ai thread di pianificazione del tempo dell'UE e gli UE trascorrono più tempo a passare da uno all'altro, rispetto a quelli effettivamente utilizzati per eseguire thread attivi di programmi. Questo è il punto di diseconomie di scala; in realtà ci vorrà più tempo per l'esecuzione di un algoritmo multithread se si dovesse aggiungere un thread aggiuntivo a questo punto.

Quindi, nel complesso, vuoi mantenere nel tuo programma almeno tanti thread quanti sono gli EU sul computer, ma vuoi evitare di avere più del doppio di quel numero che non sta aspettando o dormendo.


Se N è il numero di thread e U il numero di unità, l'OP ha messo in dubbio la regola "N = U". Lo stai rilassando secondo una regola "U <= N <= 2 U". Andrei un po 'oltre e direi che "N <= c U" per una costante "ragionevolmente piccola" (nota al programmatore) c è accettabile (se i benchmark mostrano prestazioni ragionevoli). Sarei molto preoccupato se il numero di thread può crescere fino a un numero potenzialmente illimitato.
5gon12eder

1

Dovresti usare un thread per:

Ogni processore che devi tenere occupato.

Ogni I / O può essere utilmente sospeso contemporaneamente che non è possibile eseguire in modo non bloccante. (Ad esempio, legge da un disco locale.)

Ogni attività che richiede un thread dedicato, ad esempio la chiamata in una libreria che non ha un'interfaccia non bloccante o in cui le interfacce non bloccanti non sono appropriate. Ciò include attività come il monitoraggio dell'orologio di sistema, l'attivazione dei timer e così via.

Qualche extra per proteggere da blocchi imprevisti come errori di pagina.

Qualche extra per proteggersi dai blocchi previsti che non vale la pena ottimizzare, ad esempio nel codice non critico. (Ad esempio, se molto raramente dovessi fare una richiesta DNS, probabilmente non vale la pena fare richieste DNS in modo asincrono. Basta creare alcuni thread extra e semplificarti la vita.)

Se segui la regola "un thread per processore", tutto il tuo codice è critico per le prestazioni. Qualsiasi codice che si blocca per qualche motivo significa che il tuo processo non può usare quel processore. Ciò rende la programmazione molto più difficile senza una buona ragione.


0

È possibile generare processi e thread per abilitare l'utilizzo di un sistema multicore \ multiprocessore per un singolo programma, nel qual caso non si ottiene alcun vantaggio (almeno per il singolo programma) dall'avere più thread \ processi quindi core.

Oppure puoi avere routine che eseguano il polling per un evento che in genere blocca ulteriori esecuzioni. Piuttosto, quindi collegare la CPU al polling, è possibile invece creare un thread che rimarrà in uno stato inattivo fino a quando l'evento appropriato non lo riattiva. Questo metodo è molto comunemente usato nei server Web e nelle code degli eventi della GUI. La maggior parte dei programmi vuole avere una sorta di archivio dati centrale (anche se il suo codice di esecuzione del programma) a cui tutti i thread possono accedere, quindi immagino sia per questo che usano il threading sui processi.


0

Le app che menzioni raramente eseguono tutte quelle decine di thread contemporaneamente. Molti di loro siedono lì perché sono in un pool di thread . L'app invia varie attività a una coda, che viene eliminata dai thread nel pool di thread.

Perché la piscina è così grande allora? Perché, spesso i thread devono attendere altre risorse come disco, rete, l'utente, qualche altro thread, ecc. Mentre un thread è in attesa, è opportuno eseguire altri thread per utilizzare completamente il processore. Dimensionare la piscina in modo appropriato è difficile, però. Troppi thread e perderai le prestazioni perché il processore non viene completamente utilizzato durante l'attesa di qualcosa. Troppi thread e perderai prestazioni a causa del passaggio da uno all'altro.

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.