2015-02-10 18 views
12

Questo frammento di codice è da Stephen Cleary's blog e fornisce un esempio di come segnalare il progresso quando si utilizza Task.Run. Mi piacerebbe sapere perché non ci sono problemi di cross thread con l'aggiornamento dell'interfaccia utente, con cui intendo perché non è richiesto il richiamo?Task.Run e interfaccia utente Progress Aggiornamenti

private async void button2_Click(object sender, EventArgs e) 
{ 
    var progressHandler = new Progress<string>(value => 
    { 
     label2.Text = value; 
    }); 
    var progress = progressHandler as IProgress<string>; 
    await Task.Run(() => 
    { 
     for (int i = 0; i != 100; ++i) 
     { 
      if (progress != null) 
       progress.Report("Stage " + i); 
      Thread.Sleep(100); 
     } 
    }); 
    label2.Text = "Completed."; 
} 
+0

Le concatenazioni di stringhe @newbieguy chiamano automaticamente il metodo ToString() –

risposta

19

Progress<T> cattura la corrente SynchronisationContext quando viene istanziata. Ogni volta che chiami lo Report, lo delega di nascosto al contesto catturato. Nell'esempio, il contesto acquisito è l'interfaccia utente, il che significa che non si verificano eccezioni.

+0

Sapete quale priorità di thread viene assegnata a questo?cioè: Normale, Sfondo, ApplicationIdle ecc. – Kelly

+0

@Kelly la priorità del dispatcher è Normale. Vedere la sezione dei commenti qui https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchersynchronizationcontext.post(v=vs.110).aspx – Gusdor

8

Il Progress<T> costruttore cattura la corrente SynchronizationContext oggetto.

La classe SynchronizationContext è una struttura che astrae i particolari del modello di threading coinvolti. Cioè, in Windows Form userà Control.Invoke, in WPF userà Dispatcher.Invoke, ecc

Quando l'oggetto progress.Report è chiamato, l'oggetto Progress conosce se stessa che avrebbe fatto il suo delegato con il catturata SynchronizationContext.

In altri termini, funziona perché Progress è stato progettato per gestirlo senza che lo sviluppatore debba esplicitamente dirlo.

4

Sembra siete confusi a causa del fatto che una parte di questa macchina cross-thread è nascosto da occhi degli sviluppatori in modo da avere solo per "prendere e usare": http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx

abbiamo introdotto l'interfaccia IProgress a ti consente di creare un'esperienza per la visualizzazione dei progressi. Questa interfaccia espone un metodo Report (T) , che l'attività asincrona chiama per segnalare lo stato di avanzamento. Esporre questa interfaccia nella firma del metodo asincrono e il chiamante deve fornire un oggetto che implementa questa interfaccia. Insieme, l'attività e il chiamante creano un collegamento molto utile (e potrebbe essere in esecuzione su diversi thread ).

Abbiamo inoltre previsto classe Progress, che è un'implementazione di IProgress. Siete invitati a utilizzare progresso nel vostro implementazione, perché gestisce tutta la contabilità intorno risparmio e ripristinare il contesto di sincronizzazione. Il progresso espone sia un evento sia un callback di Azione, che vengono richiamati quando i report dell'attività avanzano. Questo modello consente di scrivere codice che semplicemente reagisce alle modifiche all'avanzamento man mano che si verificano. Insieme, IProgress e Progress forniscono un modo semplice per passare le informazioni di avanzamento da un'attività sfondo al thread UI.

Solo una cosa da menzionare: la notifica di avanzamento verrà richiamato dopo la parte del lavoro è fatto, non solo in quel momento. Quindi, se il thread dell'interfaccia utente è inattivo e si dispone di core CPU di riserva, il ritardo sarà quasi pari a zero. Se il thread dell'interfaccia utente è occupato, la notifica non verrà richiamata fino a quando il thread dell'interfaccia utente non sarà tornato inattivo (indipendentemente dalla quantità di core CPU inutilizzati nel computer).