2009-04-21 10 views
5

Quando scrivo un'app gestita da un messaggio. molto simile a un'app di Windows standard solo che utilizza ampiamente la messaggistica per le operazioni interne, quale sarebbe l'approccio migliore per quanto riguarda il threading?Ulteriori thread, prestazioni migliori?

come la vedo io, ci sono fondamentalmente tre approcci (se avete qualsiasi altra configurazione in mente, si prega di condividere):

  1. avere un unico processo filo tutti i messaggi.
  2. Avere thread separati per tipi di messaggi separati (Generale, UI, Rete, ecc ...)
  3. Avere più thread che condividono ed elaborano una singola coda di messaggi.

Quindi, ci sarebbero differenze di prestazioni significative tra i tre? Ecco alcune considerazioni generali: Ovviamente, le ultime due opzioni beneficiano di una situazione in cui è presente più di un processore. Inoltre, se un thread è in attesa di un evento esterno, altri thread possono ancora elaborare messaggi non correlati. Ma ignorandolo, sembra che più thread aggiungano solo overhead (interruttori di thread, per non parlare di situazioni di sincronizzazione più complicate).

E un'altra domanda: consiglieresti di implementare tale sistema sul sistema di messaggistica standard di Windows, o di implementare un meccanismo di coda separato, e perché?

risposta

3

Non possiamo dire molto di sicuro senza conoscere il carico di lavoro (ad esempio, la distribuzione statistica degli eventi nel tempo), ma in generale

  • singola coda con più server è almeno altrettanto veloce, e di solito più veloce, quindi 1,3 sarebbe preferibile a 2.
  • più thread nella maggior parte delle lingue aggiungono complessità a causa della necessità di evitare conflitti e problemi di scrittori multipli
  • processi di lunga durata possono bloccare l'elaborazione per altre cose che potrebbero essere eseguite più veloce.

Immagino di sì a cavallo è che avere una sola coda di eventi, con diversi thread del server prendendo gli eventi dalla coda, potrebbe essere un po 'più veloce.

Assicurarsi di utilizzare una struttura dati thread-safe per la coda.

0

Un buon punto di partenza è chiedersi perché sono necessari più thread.

La risposta ben pensata a questa domanda ti porterà alla migliore risposta alla domanda successiva, "come dovrei usare più thread nella mia applicazione?"

E questa deve essere una domanda successiva; non una domanda primaria. La prima domanda deve essere perché, non come.

+0

La preoccupazione è già espressa: prestazioni. Se voleva che i thread migliorassero X, avrebbe chiesto quale soluzione offriva la migliore performance di X. – MSalters

+0

facendo cosa? –

+0

Se non sai cosa speri di migliorare in particolare, alla fine ti limiti a lanciare fili alla tua applicazione alla cieca, sperando che in qualche modo renderà le cose più veloci. Ma non funziona in questo modo e puoi facilmente rendere l'applicazione più lenta e più incline ai difetti se non hai davvero un obiettivo o un problema che stai cercando di risolvere. –

0

Penso che dipenda da quanto tempo ogni thread sarà in esecuzione. Ogni messaggio impiega lo stesso tempo per elaborare? O alcuni messaggi prenderanno alcuni secondi per esempio. Se avessi saputo che il messaggio A avrebbe impiegato 10 secondi per completare avrei sicuramente usato un nuovo thread perché dovrei voler mantenere la coda per un lungo thread in esecuzione ...

I miei 2 centesimi.

1

In generale, non preoccuparti del sovraccarico dei thread. Non sarà un problema se stai parlando solo di una manciata di loro. Le condizioni di gara, i deadlock e le contese sono una preoccupazione maggiore, e se non sai di cosa sto parlando, devi leggere molto prima di affrontare questo problema.

Vorrei andare con l'opzione 3, utilizzando qualsiasi astrazione che la mia lingua di scelta offre.

3

Tutto dipende.

Ad esempio:

  • Eventi a una coda GUI sono fatto meglio da un unico filo in quanto v'è un ordine implicito negli eventi così essi devono essere fatte in serie. Questo è il motivo per cui la maggior parte delle app della GUI ha un singolo thread per gestire gli eventi, anche se potenzialmente più eventi per crearli (e non impedisce al thread di eventi di creare un lavoro e gestirlo in un pool di worker (vedi sotto)).

  • Gli eventi su un socket possono potenzialmente essere eseguiti in parallelo (presupponendo HTTP) poiché ogni richiesta è priva di stato e può quindi essere eseguita in modo indipendente (OK, so che si sta semplificando l'HTTP).

  • Lavoro Tutti i lavori sono indipendenti e posizionati in coda. Questo è il caso classico di utilizzare un set di thread di lavoro. Ogni thread esegue un'operazione potenzialmente lunga indipendentemente dagli altri thread. Al termine ritorna in coda per un altro lavoro.

+0

Concordo, la complessità di un problema risolto utilizzando il modello sbagliato avrà probabilmente un impatto maggiore sulle prestazioni rispetto all'effettiva efficienza del meccanismo sottostante. – jturcotte

8

La scelta specifica del modello di threading deve essere guidata dalla natura del problema che si sta tentando di risolvere. Non esiste necessariamente un unico approccio "corretto" per progettare il modello di threading per tale applicazione. Tuttavia, se adottiamo le seguenti ipotesi:

  1. messaggi arrivano frequentemente
  2. messaggi sono indipendenti e non fare troppo affidamento su risorse condivise
  3. è auspicabile per rispondere a un messaggio in arrivo il più rapidamente possibile
  4. si desidera l'applicazione per scalare bene attraverso architetture di elaborazione (cioè sistemi multicodice/multi-CPU)
  5. scalabilità è il requisito chiave di design (ad esempio, più un messaggio ad un ritmo più veloce)
  6. resilienza a thread failure/long operations è auspicabile

In base alla mia esperienza, l'architettura di threading più efficace sarebbe quella di utilizzare un pool di thread. Tutti i messaggi arrivano su una singola coda, più thread attendono sulla coda ed elaborano i messaggi man mano che arrivano. Un'implementazione del pool di thread può modellare tutti e tre gli esempi di distribuzione dei thread.

# 1 processi Single Thread tutti i messaggi => pool di thread con un solo filo

# 2 Thread per i tipi di messaggio N => pool di thread con N fili, ciascuno fa capolino filo alla coda per trovare tipi di messaggi appropriati

# 3 più thread per tutti i messaggi => pool di thread con più thread

I vantaggi di questo progetto è che è possibile scalare il numero di thread nel thread in proporzione al ambiente di elaborazione o il carico messaggio. Il numero di thread può essere ridimensionato in fase di runtime per adattarsi al carico di messaggi in tempo reale che si sta verificando.

ci sono molte buone biblioteche filo pooling disponibili per la maggior parte delle piattaforme, tra cui .NET, C++/STL, Java, ecc

Per quanto riguarda la seconda domanda, se utilizzare finestre standard meccanismo di messaggio di spedizione. Questo meccanismo viene fornito con un sovraccarico significativo ed è in realtà destinato esclusivamente al pompaggio di messaggi attraverso il loop dell'interfaccia utente di un'applicazione Windows. A meno che questo non sia il problema che stai cercando di risolvere, ti sconsiglio di usarlo come una soluzione generale di invio di messaggi. Inoltre, i messaggi di Windows contengono pochissimi dati - non è un modello basato su oggetti. Ogni messaggio di Windows ha un codice e un parametro a 32 bit. Questo potrebbe non essere sufficiente per basare un modello di messaggistica pulito su. Infine, la coda dei messaggi di Windows non è progettata per gestire casi come saturazione della coda, inattività di thread o ri-accodamento dei messaggi; questi sono casi che si presentano spesso nell'implementazione di una soluzione decente per i messaggi.

+0

+1: "La scelta specifica del modello di threading deve essere guidata dalla natura del problema che si sta tentando di risolvere." Grazie. Le persone sembrano non capirlo e tendono a pensare che se aggiungono discussioni l'app andrà più veloce. –

1

Si noti che esistono due obiettivi di rendimento diversi e non è stato specificato quale obiettivo si intende raggiungere: produttività e reattività.

Se si sta scrivendo un'app GUI, l'interfaccia utente deve essere reattiva. Non ti importa quanti clic al secondo puoi elaborare, ma ti interessa mostrare qualche risposta entro un decimo di secondo (idealmente meno). Questo è uno dei motivi per cui è meglio avere un singolo thread dedicato alla gestione della GUI (altri motivi sono stati citati in altre risposte). Il thread della GUI deve sostanzialmente convertire i messaggi di Windows in oggetti di lavoro e lasciare che la coda di lavoro gestisca il lavoro pesante. Una volta che il lavoratore ha finito, notifica il thread della GUI, che aggiorna la visualizzazione per riflettere eventuali modifiche. Fa cose come dipingere una finestra, ma non rendere i dati da visualizzare. Questo dà all'applicazione una rapida "istantaneità" che è ciò che la maggior parte degli utenti desidera quando parlano di prestazioni. A loro non importa se ci vogliono 15 secondi per fare qualcosa di duro, a patto che quando cliccano su un pulsante o un menu, reagisce istantaneamente.

L'altra caratteristica di prestazione è la velocità effettiva. Questo è il numero di lavori che puoi elaborare in un determinato intervallo di tempo. Solitamente questo tipo di ottimizzazione delle prestazioni è necessario solo su applicazioni di tipo server o altre elaborazioni pesanti. Questo misura quante pagine web possono essere pubblicate in un'ora, o quanto tempo ci vuole per eseguire il rendering di un DVD. Per questi tipi di lavori, si desidera avere 1 thread attivo per CPU. Meno di questo, e stai andando a sprecare cicli di inattività. Più di questo, e i thread saranno in competizione per il tempo della CPU e inciamparono l'uno sull'altro. Dai un'occhiata al secondo grafico in questo articolo DDJ articles per il trade-off che hai a che fare. Si noti che il numero di thread ideale è superiore al numero di CPU disponibili a causa di cose come il blocco e il blocco. La chiave è il numero di thread attivi .

0

Penso che l'opzione 2 sia la migliore. Avere ogni thread facendo attività indipendenti ti darebbe i migliori risultati. Il terzo approccio può causare più ritardi se più thread eseguono operazioni di I/O come le letture del disco, la lettura di socket comuni e così via.

Se utilizzare il framework di messaggistica di Windows per l'elaborazione delle richieste dipende dal carico di lavoro che ogni thread avrebbe. Penso che Windows limiti il ​​no. di messaggi che possono essere accodati al massimo a 10000. Per la maggior parte dei casi questo non dovrebbe essere un problema. Ma se hai molti messaggi da accodare, questa potrebbe essere una cosa da tenere in considerazione.

coda separata dà un migliore controllo in un senso che si può riordinare esso nel modo desiderato (possono essere a seconda priorità)

0

Sì, ci saranno differenze di prestazioni tra le vostre scelte.

(1) introduce un collo a bottiglia per l'elaborazione dei messaggi
(3) introduce il conflitto di blocco perché è necessario sincronizzare l'accesso alla coda condivisa.

(2) sta iniziando a andare nella giusta direzione ... anche se una coda per ogni tipo di messaggio è un po 'estrema. Probabilmente ti consiglio di iniziare con una coda per ogni modello nella tua app e aggiungere code in attesa di farlo per migliorare le prestazioni.

Se ti piace l'opzione # 2, sembra che ti interesserebbe l'implementazione di SEDA architecture. Ci vorrà un po 'di lettura per capire cosa sta succedendo, ma penso che l'architettura si adatti bene alla tua linea di pensiero.

BTW, Yield è una buona implementazione ibrida in C++/Python.

0

Avrei un pool di thread che gestisce la coda dei messaggi e rende il numero di thread nel pool facilmente configurabile (forse anche in fase di esecuzione). Quindi provalo con il carico previsto.

In questo modo è possibile vedere qual è la correlazione effettiva e, se le ipotesi iniziali cambiano, è possibile modificare facilmente l'approccio.

Un approccio più sofisticato sarebbe per il sistema di introspettare i propri tratti di prestazione e adattare il suo uso di risorse, thread in particolare, come va. Probabilmente eccessivo per la maggior parte del codice di applicazione personalizzato, ma sono sicuro che ci sono prodotti che lo fanno là fuori.

Come per la domanda sugli eventi di Windows - Penso che sia probabilmente una domanda specifica per l'applicazione che non c'è una risposta giusta o sbagliata nel caso generale. Detto questo, di solito realizzo la mia coda in quanto posso adattarla alle caratteristiche specifiche del compito in questione. A volte ciò potrebbe coinvolgere eventi di routing tramite la coda dei messaggi di Windows.

Problemi correlati