2010-09-16 6 views
13

Sto creando un'applicazione multithreading in tempo reale in WPF, ma ho difficoltà ad aggiornare l'interfaccia utente.Applicazione di compravendita di azioni multithread in tempo reale WPF

Ho un thread di lavoro in background che contiene la logica che determina ciò che viene scambiato sul mercato. Quando una transazione valida viene inviata al mercato, ricevo aggiornamenti di stato su questi scambi tramite eventi nella mia finestra principale dell'applicazione. Ho altri eventi in cui ricevo aggiornamenti dei prezzi in tempo reale.

Attraverso questi eventi, rieseguo l'interfaccia utente. Ora sembra che ricevo gli eventi così rapidamente attraverso l'applicazione, che l'interfaccia utente non riesca a tenere il passo con la velocità con cui vengono ricevuti gli eventi, causando l'aggiornamento dell'interfaccia utente lentamente o per nulla. Essenzialmente l'interfaccia utente si blocca. Dopo che tutti gli eventi sono stati attivati, l'interfaccia utente diventa nuovamente reattiva. Una volta che è pienamente reattivo, l'interfaccia utente mostra i dati che mi aspetto.

La mia domanda è: come posso ottenere l'interfaccia utente per l'aggiornamento in tempo reale alla stessa velocità con cui ricevo gli eventi? Ho lottato con questo per un po 'ora, quindi ogni aiuto sarebbe apprezzato.

Grazie in anticipo!

risposta

11

Invece di fare in modo che il thread di lavoro spinga gli aggiornamenti al thread dell'interfaccia utente tramite eventi, considera che il thread dell'interfaccia utente li esegue (o esegue il polling) periodicamente. Il metodo push va bene in molte situazioni, ma presenta due principali svantaggi che stanno lavorando contro di te.

  • C'è un'operazione costosa marshalling qualche parte che trasferisce l'esecuzione di un metodo per eseguire gli aggiornamenti UI sicuro (almeno ci dovrebbe essere).
  • Il thread di lavoro consente di stabilire la frequenza con cui l'interfaccia utente deve essere aggiornata e, implicitamente, quanto lavoro deve svolgere. Può facilmente sopraffare la pompa dei messaggi.

Propongo di utilizzare una coda condivisa in cui il thread di lavoro accoderà una struttura dati contenente l'aggiornamento e il thread dell'interfaccia utente verrà deselezionato ed elaborato. È possibile fare in modo che il thread UI esegua il polling della coda in un intervallo scelto strategicamente in modo che non si impantanasse mai. La coda fungerà da buffer al posto della pompa dei messaggi dell'interfaccia utente. Si ridurrà e crescerà con la quantità di aggiornamenti in riflusso e flusso. Ecco un semplice diagramma di ciò di cui sto parlando.

[Worker-Thread] -> [Queue] -> [UI-Thread] 

Vorrei iniziare con l'approccio semplice coda di prima, ma si poteva prendere questo per il prossimo passo logico della creazione di un oleodotto in cui ci sono 3 le discussioni che partecipano al flusso di aggiornamenti. Il thread di lavoro accoda gli aggiornamenti e il thread dell'interfaccia utente li rimuove come prima. Ma è possibile aggiungere una nuova discussione al mix che gestisce il numero di aggiornamenti in attesa in coda e lo mantiene a una dimensione gestibile. Lo farà inoltrando su tutti gli aggiornamenti se la coda rimane piccola, ma passerà in modalità provvisoria e inizierà a scartare gli aggiornamenti che è possibile vivere senza o combinando molti in uno se è possibile definire un'operazione di unione ragionevole. Ecco un semplice diagramma di come questo schema potrebbe funzionare.

[Worker-Thread] -> [Queue-1] -> [Pipeline-Thread] -> [Queue-2] -> [UI-Thread] 

Anche in questo caso, iniziare con il semplice approccio a una coda.Se hai bisogno di più controllo, passa al pattern della pipeline. Ho usato entrambi con successo.

+0

Grazie Brian. Questa è la risposta che stavo cercando. Quindi, in poche parole, invece di consentire agli eventi in rilievo di aggiornare l'interfaccia utente, metterli in coda, disporre di un timer che periodicamente preleva tali aggiornamenti dalla coda e aggiorna l'interfaccia utente. In questo modo l'interfaccia utente viene aggiornata all'interno del tuo controllo e l'intervallo del timer può essere regolato in base alle mie esigenze. – c0D3l0g1c

+0

Esattamente! Lascia che sia l'UI a dettare quando è pronto per l'aggiornamento anziché trasferire la responsabilità al thread di lavoro. –

7

Probabilmente è necessario associare gli eventi ricevuti in modo che non tutti i segni di spunta risultino in un aggiornamento della GUI. Raggruppali se la tua GUI è già in fase di aggiornamento e fai in modo che la GUI elabori il batch successivo solo quando è pronto. Se il feed è ad alto volume (spesso con aggiornamenti di dati commerciali attivi) non sarà possibile creare una GUI che rifletta ogni singolo tick come il proprio trigger di aggiornamento autonomo.

+0

+1 in genere si può fare solo una quantità limitata di chiamate separate al thread dell'interfaccia utente per unità di tempo prima che smetta di essere reattivo: fare gli aggiornamenti di elementi come elenchi UI a intervalli regolari può anche aiutare immensamente ^^ –