2014-09-09 17 views
5

Ho sviluppato un progetto Windows Form in cui ho 10 compiti da svolgere e vorrei farlo in un modo async. Queste attività verranno visualizzate quando l'utente fa clic su un pulsante e chiamo un metodo async per fare ciò. Nel mio codice, ho già una lista di parametri per questi processi.Come fornire un feedback all'interfaccia utente in un metodo asincrono?

Le mie domande è:

A) Come trasformare il mio codice per eseguire tutti i processi in parallelo? (Vorrei implementare async/attendere)

B) Come fornire un feedback alla mia domanda di interfaccia utente?

Il soffietto codice è quello che ho provato:

mio pulsante per chiamare un metodo per avviare i processi

private void button1_Click(object sender, EventArgs e) 
{ 
    // almost 15 process 
    foreach (var process in Processes) 
    { 
     // call a async method to process 
     ProcessObject(process); 
    } 
} 

Il metodo per simulare il mio processo di ottenere un parametro

private async void ProcessObject(ProcessViewModel process) 
{ 
    // this is my loop scope, which I need to run in parallel 
    { 
     // my code is here 

     // increment the progress of this process 
     process.Progress++; 

     // feedback to UI (accessing the UI controls) 
     UpdateRow(process); 
    } 
} 

Ho provato questo, ma non sono sicuro se è il modo giusto per aggiornare la mia interfaccia utente (una griglia).

private void UpdateRow(ProcessViewModel process) 
{ 
    dataGridView1.Rows[process.Index - 1].Cells[1].Value = process.Progress; 
    dataGridView1.Refresh(); 
} 
+0

Non si è in attesa di 'ProcessObject'. Typo o ..? –

+0

No, sto solo chiamando il metodo ProcessObject. Non ho un risultato per questo metodo, dovrei cambiarlo in attesa? –

+0

Questo può essere fatto anche con TPL: http://stackoverflow.com/questions/16913560/want-to-use-task-parallel-library-with-progress-reporting-for-updating-database – Schwarzie2478

risposta

6

Prima di tutto, dicono non a async void metodi (gestori di eventi sono eccezioni), in quanto le eccezioni sollevate al suo interno non sarà notato. Usa invece async Task e attendi.

private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier 
{ 
    // almost 15 process 
    foreach (var process in Processes) 
    { 
     // call a async method to process 
     await ProcessObject(process); 
    } 
} 

private async Task ProcessObject(ProcessViewModel process)// <--Note the return type 
{ 
    // my code is here with some loops 
    await Task.Run(()=> 
    { 
     //Will be run in ThreadPool thread 
     //Do whatever cpu bound work here 
    }); 

    //At this point code runs in UI thread 
    process.Progress++; 

    // feedback to UI 
    UpdateRow(process); 
} 

Tuttavia, questo inizierà un solo compito alla volta, una volta fatto ciò che avrà inizio il prossimo. Se vuoi avviarli tutti in una volta puoi avviarli e usare Task.WhenAll per attenderli.

private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier 
{ 
    var tasks = Processes.Select(ProcessObject).ToList(); 
    await Task.WhenAll(tasks); 
} 
+0

Devo mettere la chiamata di UpdateRow all'interno di Task.Run perché avrò un ciclo in questo metodo di processo. Ho provato qui, ma ottengo un errore dicendo che il mio controllo DataGridView è accessibile dal thread perché è stato creato da un altro thread.Ho provato a passare il riferimento di dataGridView nel mio parametro ma non funziona, come posso risolvere questo? Grazie per anser –

+0

Ho apportato alcune modifiche nella mia anser –

+0

Se non ti dispiace postare il codice originale. Ho risposto in base al tuo codice, ma ora dici che devi usare 'UpdateRow' in' Task.Run'. Quello non funzionerà. Deve essere chiamato nel thread principale. Inoltre, perché hai bisogno di un ciclo lì? Fornire maggiori informazioni per favore. –

8

Sriram coperto come fare l'async/attendono in his answer, però ho voluto mostrare un altro modo per aggiornare i progressi sul thread dell'interfaccia utente utilizzando la sua risposta come base.

.NET 4.5 ha aggiunto l'interfaccia IProgress<T> e la classe Progress<T>. La classe Built in acquisisce il contesto di sincronizzazione, proprio come async/await, quindi quando vai a segnalare i tuoi progressi usa quel contesto per fare il callback (il thread dell'interfaccia utente nel tuo caso).

private async void button1_Click(object sender, EventArgs e) 
{ 
    var progress = new Progress<ProcessViewModel>(UpdateRow); //This makes a callback to UpdateRow when progress is reported. 

    var tasks = Processes.Select(process => ProcessObject(process, progress)).ToList(); 
    await Task.WhenAll(tasks); 
} 

private async Task ProcessObject(ProcessViewModel process, IProgress<ProcessViewModel> progress) 
{ 
    // my code is here with some loops 
    await Task.Run(()=> 
    { 
     //Will be run in ThreadPool thread 
     //Do whatever cpu bound work here 


     //Still in the thread pool thread 
     process.Progress++; 

     // feedback to UI, calls UpdateRow on the UI thread. 
     progress.Report(process); 
    }); 
} 
+0

Scott: ci ho pensato, ma è poco utile nel metodo asincrono. È possibile chiamare direttamente 'UpdateRow' all'interno di 'ProcessObject' quando viene ripristinato il metodo asincrono. –

+0

@SriramSakthivel Sono d'accordo che il tuo metodo è il metodo migliore (ecco perché la tua risposta è la prima cosa che ho menzionato nella mia risposta), ma l'OP sembrava essere resistente, volevo solo dare altre opzioni –

+1

Ummm, +1 per il tuo altro opzione :) –

Problemi correlati