Nella programmazione di applicazioni Windows è frequente l'uso di operazioni asincrone per evitare il blocco dell'interfaccia. Utilizzare il pattern Begin/End Invoke o il ThreadPool sono le tecniche più comuni e permettono di accodare la richiesta e di eseguirla non appena un thread è libero. L'alternativa è quella di creare un Thread personalizzato ed eseguire un delegate, ma in ogni caso come per il thread pool non si ha a disposizione un controllo delle operazioni nel caso queste siano più di una e necessittano un raggruppamento logico tale da eseguirle una dietro l'altra.
L'esigenza che si ha è quella quindi di avere un unico thread dove accodare le operazioni da eseguire una dopo l'altra. In WPF per coprire questa esigenza si può utilizzare il Dispatcher che, normalmente utilizzato per le operazioni di UI sul thread principale, può essere sfruttato anche su altri thread per innescare una coda mono thread.
Il Dispatcher non è altro che un loop infinito che gira all'interno di un thread in attesa di eseguire operazioni, perciò quello che serve per crearne uno personalizzato è istanziare prima di tutto un Thread nel quale girare:
public class OperationsQueue { private Thread queueThread; private Dispatcher dispatcher; private ManualResetEvent createEvent = new ManualResetEvent(false); public OperationsQueue() { // Creo il thread queueThread = new Thread(RunQueue); queueThread.IsBackground = true; queueThread.Start(); // Aspetto che il dispatcher sia avviato createEvent.WaitOne(); }
Il ManualResetEvent è un semaforo e viene utilizzato per accertarsi che il Dispatcher parta e sia pronto all'uso. Il metodo che infatti si invoca nel nuovo Thread esegue la funzione Run per avviare il loop e notificare l'avvio.
private void RunQueue() { // Riferimento al dispatcher in base al thread corrente dispatcher = Dispatcher.CurrentDispatcher; // Notifico che il motore è partito dispatcher.BeginInvoke(new Action(() => createEvent.Set())); // Avvio il loop, da qua il thread non esce Dispatcher.Run(); }
A questo punto è tutto pronto ed è possibile fornire la classe di un metodo per l'accodamento delle operazioni, semplicemente invocando BeginInvoke.
public void QueueInternal(Action action) { this.dispatcher.BeginInvoke(action, priority); }
L'uso del Dispatcher offre inoltre molteplici vantaggi: oltre ad un sistema solido, possiamo gestire le priorità delle operazioni, abortirle se non sono state ancora eseguite e gestire in modo univoco gli eventuali errori generati. Infine, essendo il Thread di tipo Background, una volta che il processo viene chiuso anche la coda muore con esso.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.