5

Sto cercando un modello di progettazione che si adatti al mio design dell'applicazione.Barra di avanzamento e più thread, disaccoppiamento della GUI e della logica: quale modello di progettazione sarebbe il migliore?

La mia applicazione elabora grandi quantità di dati e produce alcuni grafici. L'elaborazione dei dati (recupero da file, calcoli intensivi della CPU) e operazioni con i grafici (disegno, aggiornamento) avvengono in thread separati.

Il grafico può scorrere: in questo caso è necessario elaborare nuove porzioni di dati. Poiché in un grafico possono essere presenti più serie, è possibile generare più thread (due thread per serie, uno per l'aggiornamento del set di dati e uno per l'aggiornamento del grafico).

Non voglio creare più barre di avanzamento. Invece, mi piacerebbe avere un'unica barra di progresso che informa sui progressi globali. Al momento posso pensare a MVC e Observer/Observable, ma è un po 'sfocato :) Forse qualcuno potrebbe indicarmi la giusta direzione, grazie.

risposta

1

Più barre di avanzamento non sono una cattiva idea, attenzione. O forse una barra di avanzamento complessa che mostra diversi thread in esecuzione (come i programmi di download manager a volte hanno). Finché l'interfaccia utente è intuitiva, i tuoi utenti apprezzeranno i dati aggiuntivi.

Quando cerco di rispondere a tali domande di progettazione, cerco innanzitutto di analizzare problemi analoghi o analoghi in altre applicazioni e in che modo vengono risolti. Quindi ti suggerisco di fare qualche ricerca considerando altre applicazioni che mostrano progressi complessi (come l'esempio di download manager) e prova ad adattare una soluzione esistente alla tua applicazione.

Mi spiace non posso offrire un design più specifico, questo è solo un consiglio generale. :)

1

Stick con Observer/Observable per questo genere di cose. Alcuni oggetti osservano le varie serie che elaborano i thread e lo stato dei report aggiornando la barra di riepilogo.

6

Una volta ho passato la parte migliore di una settimana cercando di eseguire una barra di avanzamento uniforme e non incerta su un algoritmo molto complesso.

L'algoritmo aveva 6 passaggi diversi. Ogni passo aveva caratteristiche temporali che dipendevano seriamente da A) i dati sottostanti in elaborazione, non solo la "quantità" di dati ma anche il "tipo" di dati e B) 2 dei passi scalati estremamente bene con l'aumento del numero di cpus, 2 passaggi sono stati eseguiti in 2 thread e 2 passaggi erano effettivamente a thread singolo.

Il mix di dati ha avuto un impatto molto maggiore sul tempo di esecuzione di ogni passaggio rispetto al numero di core.

La soluzione che alla fine ha risolto era davvero piuttosto semplice. Ho eseguito 6 funzioni che hanno analizzato il set di dati e cercato di prevedere il tempo di esecuzione effettivo di ogni fase di analisi. L'euristica in ciascuna funzione ha analizzato sia i set di dati in analisi che il numero di cpu. In base ai dati di runtime della mia macchina a 4 core, ciascuna funzione ha sostanzialmente restituito il numero di millisecondi previsto, sulla mia macchina.

f1 (..) + f2 (..) + F3 (..) + F4 (..) + F5 (..) + F6 (..) = tempo di esecuzione totale in millisecondi

Ora date queste informazioni, è possibile sapere in modo efficace quale percentuale del tempo di esecuzione totale deve essere assunta da ciascun passaggio. Ora se dici che step1 dovrebbe prendere il 40% del tempo di esecuzione, in pratica devi scoprire come emettere 40 eventi dell'1% da quell'algoritmo.Dicono che il ciclo for sta elaborando 100.000 articoli, probabilmente si potrebbe fare:

for (int i = 0; i < numItems; i++){ 
    if (i % (numItems/percentageOfTotalForThisStep) == 0) emitProgressEvent(); 
    .. do the actual processing .. 
} 

Questo algoritmo ci ha dato una setosa barra di avanzamento liscia che funzionato in modo impeccabile. La tecnologia di implementazione può avere diverse forme di ridimensionamento e funzionalità disponibili nella barra di avanzamento, ma il modo di pensare basilare sul problema è lo stesso.

E sì, non importava davvero che i numeri di riferimento euristici fossero stati elaborati sulla mia macchina - l'unico vero problema è se si desidera modificare i numeri quando si esegue su una macchina diversa. Ma tu conosci ancora il rapporto (che è l'unica cosa veramente importante qui), così puoi vedere come il tuo hardware locale funziona in modo diverso da quello che avevo.

Ora il lettore di SO medio può chiedersi perché mai qualcuno potrebbe trascorrere una settimana facendo una barra di avanzamento senza problemi. La funzione è stata richiesta dal responsabile commerciale, e credo che l'abbia usata nelle riunioni di vendita per ottenere contratti. Denaro parla;)

+1

In aumento per il fresco. – erikprice

2

In situazioni con thread o processi asincroni/attività come questa, trovo utile avere un tipo o un oggetto astratto nel thread principale che rappresenta (e incapsula idealmente) ogni processo. Quindi, per ogni thread di lavoro, ci sarà presumibilmente un oggetto (chiamiamolo Operation) nel thread principale per gestire quel worker, e ovviamente ci sarà una sorta di struttura di dati tipo elenco per contenere queste operazioni.

Ove applicabile, ciascuna operazione fornisce i metodi di avvio/arresto per il proprio operatore e, in alcuni casi, come il proprio, proprietà numeriche che rappresentano lo stato di avanzamento e il tempo totale previsto o il lavoro dell'attività di quella particolare operazione. Le unità non devono necessariamente essere basate sul tempo, se sai che eseguirai 6,230 calcoli, puoi semplicemente pensare a queste proprietà come conteggi di calcolo. Inoltre, ogni task dovrà avere un modo per aggiornare l'Operazione proprietaria dei suoi attuali progressi in qualunque meccanismo sia appropriato (callback, chiusure, invio di eventi o qualsiasi altro meccanismo fornito dal proprio linguaggio di programmazione/framework di threading).

Così mentre il tuo lavoro effettivo viene eseguito in thread separati, un oggetto Operazione corrispondente nel thread "principale" viene continuamente aggiornato/notificato del progresso del suo lavoratore. La barra di avanzamento può aggiornarsi di conseguenza, mappando il totale dei tempi "previsti" delle operazioni sul totale, e il totale dei tempi di "avanzamento" delle operazioni fino al suo stato attuale, in qualsiasi modo abbia senso per il framework della barra di avanzamento.

Ovviamente ci sono un sacco di altre considerazioni/lavoro che è necessario fare concretamente nell'implementazione di questo, ma spero che questo vi dia il senso di ciò.