Procedura dettagliata: implementazione di un form che utilizza un'operazione in background
Aggiornamento: novembre 2007
Per evitare che l'interfaccia utente (UI, User Interface) si blocchi o risponda in ritardo a causa di un'operazione che impiega molto tempo per essere completata, è possibile utilizzare la classe BackgroundWorker per eseguire l'operazione in un altro thread.
In questa procedura dettagliata viene descritto come usare la classe BackgroundWorker per eseguire "in background" i calcoli dispendiosi in termini di tempo lasciando inalterata la capacità di risposta dell'interfaccia utente. Nel corso della procedura, un'applicazione calcola i numeri Fibonacci in modo asincrono. Sebbene il calcolo di un numero Fibonacci a molte cifre possa impiegare una notevole quantità di tempo, il thread principale della UI non verrà interrotto da questa operazione e la capacità di risposta del form resterà inalterata durante il calcolo.
Di seguito sono elencate le attività illustrate nella procedura dettagliata:
Creazione di un'applicazione basata su Windows
Creazione di BackgroundWorker nel form
Aggiunta dei gestori eventi asincroni
Aggiunta dei report sullo stato di avanzamento e supporto per l'annullamento
Per il listato completo del codice utilizzato in questo esempio, vedere Procedura: implementare un form che utilizza un'operazione in background.
Nota: |
---|
È possibile che le finestre di dialogo e i comandi di menu visualizzati varino da quelli descritti nella Guida in linea a seconda delle impostazioni attive o dell'edizione del programma Per modificare le impostazioni, scegliere Importa/esporta impostazioni dal menu Strumenti. Per ulteriori informazioni, vedere Impostazioni di Visual Studio. |
Creazione del progetto
Il primo passaggio indica come creare il progetto e impostare il form.
Per creare un form che utilizza un'operazione in background
Creare un progetto di applicazione basata su Windows chiamato BackgroundWorkerExample. Per informazioni dettagliate, vedere Procedura: creare un progetto applicazione Windows.
In Esplora soluzioni fare clic con il pulsante destro del mouse su Form1 e scegliere Rinomina dal menu di scelta rapida. Modificare il nome file in FibonacciCalculator. Scegliere il pulsante Sì quando richiesto per rinominare tutti i riferimenti all'elemento di codice 'Form1'.
Trascinare un controllo NumericUpDown dalla Casella degli strumenti nel form. Impostare la proprietà Minimum su 1 e la proprietà Maximum su 91.
Aggiungere due controlli Button nel form.
Rinominare il primo controllo Button in startAsyncButton e impostare la proprietà Text su Start Async. Rinominare il secondo controllo Button in cancelAsyncButton e impostare la proprietà Text su Cancel Async. Impostarne la proprietà Enabled su false.
Creare un gestore per entrambi gli eventi Click del controllo Button. Per informazioni dettagliate, vedere Procedura: creare le impostazioni delle applicazioni utilizzando la finestra di progettazione.
Trascinare un controllo Label dalla Casella degli strumenti nel form e rinominarlo resultLabel.
Trascinare un controllo ProgressBar dalla Casella degli strumenti nel form.
Creazione di un BackgroundWorker nel form
È possibile creare il BackgroundWorker per l'operazione asincrona utilizzando ProgettazioneWindows Form.
Per creare un BackgroundWorker con la finestra di progettazione
- Dalla scheda Componenti della Casella degli strumenti trascinare un componente BackgroundWorker nel form.
Aggiunta dei gestori eventi asincroni
A questo punto è possibile aggiungere i gestori per gli eventi asincroni del componente BackgroundWorker. L'operazione dispendiosa in termini di tempo che verrà eseguita in background, ossia quella che calcola i numeri Fibonacci, viene chiamata da uno di questi gestori eventi.
Per implementare i gestori eventi asincroni
Fare clic sul pulsante Eventi nella finestra Proprietà. Per creare i gestori eventi, fare doppio clic sugli eventi DoWork e RunWorkerCompleted. Per ulteriori informazioni sull'utilizzo dei gestori eventi, vedere Procedura: creare le impostazioni delle applicazioni utilizzando la finestra di progettazione.
Creare nel form un nuovo metodo denominato ComputeFibonacci. L'operazione verrà effettivamente svolta da questo metodo che verrà eseguito in background. Il codice dimostra l'implementazione ricorsiva dell'algoritmo Fibonacci che è decisamente inefficiente e esponenzialmente impiega più tempo per completare i numeri a molte cifre. Viene impiegato per dimostrare come un'operazione possa provocare lunghi ritardi nell'applicazione.
' This is the method that does the actual work. For this ' example, it computes a Fibonacci number and ' reports progress as it does its work. Function ComputeFibonacci( _ ByVal n As Integer, _ ByVal worker As BackgroundWorker, _ ByVal e As DoWorkEventArgs) As Long ' The parameter n must be >= 0 and <= 91. ' Fib(n), with n > 91, overflows a long. If n < 0 OrElse n > 91 Then Throw New ArgumentException( _ "value must be >= 0 and <= 91", "n") End If Dim result As Long = 0 ' Abort the operation if the user has canceled. ' Note that a call to CancelAsync may have set ' CancellationPending to true just after the ' last invocation of this method exits, so this ' code will not have the opportunity to set the ' DoWorkEventArgs.Cancel flag to true. This means ' that RunWorkerCompletedEventArgs.Cancelled will ' not be set to true in your RunWorkerCompleted ' event handler. This is a race condition. If worker.CancellationPending Then e.Cancel = True Else If n < 2 Then result = 1 Else result = ComputeFibonacci(n - 1, worker, e) + _ ComputeFibonacci(n - 2, worker, e) End If ' Report progress as a percentage of the total task. Dim percentComplete As Integer = _ CSng(n) / CSng(numberToCompute) * 100 If percentComplete > highestPercentageReached Then highestPercentageReached = percentComplete worker.ReportProgress(percentComplete) End If End If Return result End Function
// This is the method that does the actual work. For this // example, it computes a Fibonacci number and // reports progress as it does its work. long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e) { // The parameter n must be >= 0 and <= 91. // Fib(n), with n > 91, overflows a long. if ((n < 0) || (n > 91)) { throw new ArgumentException( "value must be >= 0 and <= 91", "n"); } long result = 0; // Abort the operation if the user has canceled. // Note that a call to CancelAsync may have set // CancellationPending to true just after the // last invocation of this method exits, so this // code will not have the opportunity to set the // DoWorkEventArgs.Cancel flag to true. This means // that RunWorkerCompletedEventArgs.Cancelled will // not be set to true in your RunWorkerCompleted // event handler. This is a race condition. if (worker.CancellationPending) { e.Cancel = true; } else { if (n < 2) { result = 1; } else { result = ComputeFibonacci(n - 1, worker, e) + ComputeFibonacci(n - 2, worker, e); } // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if (percentComplete > highestPercentageReached) { highestPercentageReached = percentComplete; worker.ReportProgress(percentComplete); } } return result; }
// This is the method that does the actual work. For this // example, it computes a Fibonacci number and // reports progress as it does its work. long ComputeFibonacci( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e ) { // The parameter n must be >= 0 and <= 91. // Fib(n), with n > 91, overflows a long. if ( (n < 0) || (n > 91) ) { throw gcnew ArgumentException( "value must be >= 0 and <= 91","n" ); } long result = 0; // Abort the operation if the user has cancelled. // Note that a call to CancelAsync may have set // CancellationPending to true just after the // last invocation of this method exits, so this // code will not have the opportunity to set the // DoWorkEventArgs.Cancel flag to true. This means // that RunWorkerCompletedEventArgs.Cancelled will // not be set to true in your RunWorkerCompleted // event handler. This is a race condition. if ( worker->CancellationPending ) { e->Cancel = true; } else { if ( n < 2 ) { result = 1; } else { result = ComputeFibonacci( n - 1, worker, e ) + ComputeFibonacci( n - 2, worker, e ); } // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if ( percentComplete > highestPercentageReached ) { highestPercentageReached = percentComplete; worker->ReportProgress( percentComplete ); } } return result; }
// This is the method that does the actual work. For this // example, it computes a Fibonacci number and // reports progress as it does its work. long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e) { // The parameter n must be >= 0 and <= 91. // Fib(n), with n > 91, overflows a long. if (n < 0 || n > 91) { throw new ArgumentException("value must be >= 0 and <= 91", "n"); } long result = 0; // Abort the operation if the user has cancelled. // Note that a call to CancelAsync may have set // CancellationPending to true just after the // last invocation of this method exits, so this // code will not have the opportunity to set the // DoWorkEventArgs.Cancel flag to true. This means // that RunWorkerCompletedEventArgs.Cancelled will // not be set to true in your RunWorkerCompleted // event handler. This is a race condition. if (worker.get_CancellationPending()) { e.set_Cancel(true); } else { if (n < 2) { result = 1; } else { result = ComputeFibonacci(n - 1, worker, e) + ComputeFibonacci(n - 2, worker, e); } // Report progress as a percentage of the total task. int percentComplete=(int)((float)(n)/(float)(numberToCompute)* 100); if (percentComplete > highestPercentageReached) { highestPercentageReached = percentComplete; worker.ReportProgress(percentComplete); } } return result; }
Nel gestore eventi DoWork aggiungere una chiamata al metodo ComputeFibonacci. Richiamare il primo parametro per ComputeFibonacci dalla proprietà Argument di DoWorkEventArgs. I parametri BackgroundWorker e DoWorkEventArgs verranno utilizzati in seguito per i report sullo stato di avanzamento e il supporto per l'annullamento.
Nota: |
---|
È importante che il gestore dell'evento DoWork non faccia direttamente riferimento alla variabile dell'istanza backgroundWorker1 in quanto altrimenti verrebbe accoppiato a una specifica istanza di BackgroundWorker. Il riferimento al BackgroundWorker che ha generato l'evento viene invece recuperato dal parametro sender. Ciò è importante quando il form include più di un BackgroundWorker. |
Assegnare il valore restituito da ComputeFibonacci alla proprietà Result di DoWorkEventArgs. Il risultato sarà disponibile al gestore dell'evento RunWorkerCompleted.
Aggiunta dei report sullo stato di avanzamento e supporto per l'annullamento
Per le operazioni asincrone che impiegano molto tempo è spesso opportuno notificare all'utente lo stato di avanzamento e permettegli di annullare eventualmente l'operazione. La classe BackgroundWorker fornisce un evento che consente di inviare lo stato mentre l'operazione procede in background. Fornisce inoltre un flag che consente al codice di rilevare una chiamata a CancelAsync e interrompersi.
Per implementare i report sullo stato di avanzamento
Nella finestra Proprietà selezionare backgroundWorker1. Impostare le proprietà WorkerReportsProgress e WorkerSupportsCancellation su true.
Nel form FibonacciCalculator dichiarare due variabili che verranno utilizzate per tenere traccia dello stato di avanzamento.
Private numberToCompute As Integer = 0 Private highestPercentageReached As Integer = 0
private int numberToCompute = 0; private int highestPercentageReached = 0;
int numberToCompute; int highestPercentageReached;
private int numberToCompute = 0; private int highestPercentageReached = 0;
Aggiunge un gestore eventi per l'evento ProgressChanged. Nel gestore dell'evento ProgressChanged aggiornare ProgressBar con la proprietà ProgressPercentage del parametro ProgressChangedEventArgs.
' This event handler updates the progress bar. Private Sub backgroundWorker1_ProgressChanged( _ ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _ Handles backgroundWorker1.ProgressChanged Me.progressBar1.Value = e.ProgressPercentage End Sub
// This event handler updates the progress bar. private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; }
// This event handler updates the progress bar. void backgroundWorker1_ProgressChanged( Object^ /*sender*/, ProgressChangedEventArgs^ e ) { this->progressBar1->Value = e->ProgressPercentage; }
// This event handler updates the progress bar. private void backgroundWorker1_ProgressChanged(Object sender, ProgressChangedEventArgs e) { this.progressBar1.set_Value(e.get_ProgressPercentage()); } //backgroundWorker1_ProgressChanged
Per implementare il supporto per l'annullamento
Nel gestore dell'evento Click del controllo cancelAsyncButton, aggiungere il codice per annullare l'operazione asincrona.
Private Sub cancelAsyncButton_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cancelAsyncButton.Click ' Cancel the asynchronous operation. Me.backgroundWorker1.CancelAsync() ' Disable the Cancel button. cancelAsyncButton.Enabled = False End Sub 'cancelAsyncButton_Click
private void cancelAsyncButton_Click(System.Object sender, System.EventArgs e) { // Cancel the asynchronous operation. this.backgroundWorker1.CancelAsync(); // Disable the Cancel button. cancelAsyncButton.Enabled = false; }
void cancelAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ ) { // Cancel the asynchronous operation. this->backgroundWorker1->CancelAsync(); // Disable the Cancel button. cancelAsyncButton->Enabled = false; }
private void cancelAsyncButton_Click(Object sender, System.EventArgs e) { // Cancel the asynchronous operation. this.backgroundWorker1.CancelAsync(); // Disable the Cancel button. cancelAsyncButton.set_Enabled(false); }
I seguenti frammenti di codice nel metodo ComputeFibonacci restituiscono lo stato di avanzamento e supportano l'annullamento.
If worker.CancellationPending Then e.Cancel = True ... ' Report progress as a percentage of the total task. Dim percentComplete As Integer = _ CSng(n) / CSng(numberToCompute) * 100 If percentComplete > highestPercentageReached Then highestPercentageReached = percentComplete worker.ReportProgress(percentComplete) End If
if (worker.CancellationPending) { e.Cancel = true; } ... // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if (percentComplete > highestPercentageReached) { highestPercentageReached = percentComplete; worker.ReportProgress(percentComplete); }
if ( worker->CancellationPending ) { e->Cancel = true; } ... // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if ( percentComplete > highestPercentageReached ) { highestPercentageReached = percentComplete; worker->ReportProgress( percentComplete ); }
if (worker.get_CancellationPending()) { e.set_Cancel(true); } ... // Report progress as a percentage of the total task. int percentComplete=(int)((float)(n)/(float)(numberToCompute)* 100); if (percentComplete > highestPercentageReached) { highestPercentageReached = percentComplete; worker.ReportProgress(percentComplete); }
Verifica
A questo punto è possibile compilare ed eseguire l'applicazione Fibonacci Calculator.
Per eseguire il test del progetto
Premere F5 per compilare ed eseguire l'applicazione.
Durante l'esecuzione del calcolo in background, la ProgressBar visualizzerà lo stato di avanzamento del calcolo in relazione alla completamento. È anche possibile annullare l'operazione in sospeso.
Per i numeri con poche cifre, il calcolo dovrebbe essere molto rapido, ma per i numeri con tante cifre, si potrebbe notare un considerevole ritardo. Se si immette il valore 30 o superiore, il ritardo sarà di diversi secondi, a seconda della velocità del computer. Per i valori maggiori di 40, potrebbero essere necessari diversi minuti o ore per terminare il calcolo. Mentre il calcolatore è impegnato a calcolare un numero Fibonacci con tante cifre, il form può essere spostato liberamente, ridotto a icona, ingrandito e persino chiuso in quanto il thread principale della UI non è in attesa della fine del calcolo.
Passaggi successivi
Una volta implementato un form che utilizza un componente BackgroundWorker per eseguire un calcolo in background, si possono sperimentare altre possibilità per le operazioni asincrone.
Utilizzare più oggetti BackgroundWorker per diverse operazioni simultanee.
Per eseguire il debug dell'applicazione con multithreading, vedere Procedura: utilizzare la finestra Thread.
Implementare il componente che supporta il modello di programmazione asincrona. Per ulteriori informazioni, vedere Cenni preliminari sul modello asincrono basato su eventi.
Attenzione: Quando si utilizza il multithreading, si è potenzialmente esposti al rischio di errori molto seri e complicati. Consultare Suggerimenti per l'utilizzo del threading gestito prima di implementare soluzioni che utilizzano il multithreading.
Vedere anche
Attività
Procedura: implementare un form che utilizza un'operazione in background
Procedura dettagliata: esecuzione di un'operazione in background
Concetti
Suggerimenti per l'utilizzo del threading gestito