wpf
Thread Affinity Accessing UI Elements
Ricerca…
Accesso a un elemento dell'interfaccia utente all'interno di un'attività
Tutti gli elementi dell'interfaccia utente creati e risiedono nel thread principale di un programma. L'accesso a questi da un altro thread è vietato dal .net framework runtime. Fondamentalmente è perché tutti gli elementi dell'interfaccia utente sono risorse thread-sensitive e l'accesso a una risorsa in un ambiente multi-thread richiede di essere thread-safe. Se questo accesso agli oggetti incrociati è consentito, la consistenza sarà in primo luogo compromessa.
Considera questo scenario:
Abbiamo un calcolo che si verifica all'interno di un'attività. Le attività vengono eseguite in un altro thread rispetto al thread principale. Mentre il calcolo continua, dobbiamo aggiornare una barra di avanzamento. Per fare questo:
//Prepare the action
Action taskAction = new Action( () => {
int progress = 0;
Action invokeAction = new Action( () => { progressBar.Value = progress; });
while (progress <= 100) {
progress = CalculateSomething();
progressBar.Dispatcher.Invoke( invokeAction );
}
} );
//After .net 4.5
Task.Run( taskAction );
//Before .net 4.5
Task.Factory.StartNew( taskAction ,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
Ogni elemento dell'interfaccia utente ha un oggetto Dispatcher che proviene dal suo antenato DispatcherObject
(all'interno dello spazio dei nomi System.Windows.Threading
). Dispatcher esegue il delegato specificato in modo sincrono alla priorità specificata sul thread a cui è associato il Dispatcher. Poiché l'esecuzione è sincronizzata, l'attività del chiamante deve attendere il risultato. Questo ci dà l'opportunità di utilizzare i int progress
anche all'interno di un delegato del dispatcher.
Potremmo voler aggiornare un elemento dell'interfaccia utente in modo asincrono, quindi invokeAction
modifiche alla definizione invokeAction
:
//Prepare the action
Action taskAction = new Action( () => {
int progress = 0;
Action<int> invokeAction = new Action<int>( (i) => { progressBar.Value = i; } )
while (progress <= 100) {
progress = CalculateSomething();
progressBar.Dispatcher.BeginInvoke(
invokeAction,
progress );
}
} );
//After .net 4.5
Task.Run( taskAction );
//Before .net 4.5
Task.Factory.StartNew( taskAction ,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
Questa volta abbiamo impacchettato int progress
e lo usiamo come parametro per i delegati.