Bagikan melalui


Panduan: Menerapkan Formulir yang Menggunakan Operasi Latar Belakang

Jika Anda memiliki operasi yang akan memakan waktu lama untuk diselesaikan, dan Anda tidak ingin antarmuka pengguna (UI) berhenti merespons atau memblokir, Anda dapat menggunakan BackgroundWorker kelas untuk menjalankan operasi pada utas lain.

Panduan ini menggambarkan cara menggunakan BackgroundWorker kelas untuk melakukan komputasi yang memakan waktu "di latar belakang", sementara antarmuka pengguna tetap responsif. Ketika Anda selesai, Anda akan memiliki aplikasi yang menghitung angka Fibonacci secara asinkron. Meskipun komputasi nomor Fibonacci yang besar dapat memakan waktu yang nyata, utas UI utama tidak akan terganggu oleh penundaan ini, dan formulir akan responsif selama perhitungan.

Tugas yang diilustrasikan dalam panduan ini meliputi:

  • Membuat Aplikasi berbasis Windows

  • Membuat BackgroundWorker dalam Formulir Anda

  • Menambahkan Penanganan Aktivitas Asinkron

  • Menambahkan Pelaporan Kemajuan dan Dukungan untuk Pembatalan

Untuk daftar lengkap kode yang digunakan dalam contoh ini, lihat Cara: Menerapkan Formulir yang Menggunakan Operasi Latar Belakang.

Membuat formulir yang menggunakan operasi latar belakang

  1. Di Visual Studio, buat proyek aplikasi berbasis Windows yang disebut BackgroundWorkerExample (File>New>Project>Visual C# atau Visual Basic>Classic Desktop> Formulir Windows Application).

  2. Di Penjelajah Solusi, klik kanan Formulir1 dan pilih Ganti Nama dari menu pintasan. Ubah nama file menjadi FibonacciCalculator. Klik tombol Ya saat Anda ditanya apakah Anda ingin mengganti nama semua referensi ke elemen kode 'Form1'.

  3. NumericUpDown Seret kontrol dari Kotak Alat ke formulir. Atur Minimum properti ke 1 dan Maximum properti ke 91.

  4. Tambahkan dua Button kontrol ke formulir.

  5. Ganti nama kontrol pertama Button dan atur properti ke Text Start Async.startAsyncButton Ganti nama kontrol kedua Button , dan atur properti ke Text Cancel Async.cancelAsyncButton Atur propertinya Enabled ke false.

  6. Buat penanganan aktivitas untuk kedua Button peristiwa kontrol Click . Untuk detailnya, lihat Cara: Membuat Penanganan Aktivitas Menggunakan Perancang.

  7. Label Seret kontrol dari Kotak Alat ke formulir dan ganti namanya resultLabel.

  8. ProgressBar Seret kontrol dari Kotak Alat ke formulir.

Membuat BackgroundWorker dengan Perancang

Anda dapat membuat BackgroundWorker untuk operasi asinkron Anda menggunakan Windows Forms Designer.

Dari tab Komponen kotak Alat, seret BackgroundWorker ke formulir.

Menambahkan penanganan aktivitas asinkron

Anda sekarang siap untuk menambahkan penanganan aktivitas untuk BackgroundWorker peristiwa asinkron komponen. Operasi yang memakan waktu yang akan berjalan di latar belakang, yang menghitung angka Fibonacci, dipanggil oleh salah satu penanganan aktivitas ini.

  1. Di jendela Properti , dengan BackgroundWorker komponen masih dipilih, klik tombol Peristiwa . DoWork Klik dua kali peristiwa dan RunWorkerCompleted untuk membuat penanganan aktivitas. Untuk informasi selengkapnya tentang cara menggunakan penanganan aktivitas, lihat Cara: Membuat Penanganan Aktivitas Menggunakan Perancang.

  2. Buat metode baru, yang disebut ComputeFibonacci, dalam formulir Anda. Metode ini melakukan pekerjaan aktual, dan akan berjalan di latar belakang. Kode ini menunjukkan implementasi rekursif algoritma Fibonacci, yang terutama tidak efisien, membutuhkan waktu yang jauh lebih lama untuk diselesaikan untuk jumlah yang lebih besar. Ini digunakan di sini untuk tujuan ilustrasi, untuk menunjukkan operasi yang dapat memperkenalkan penundaan panjang dalam aplikasi Anda.

    // 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 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.
    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
    
  3. Di penanganan DoWork aktivitas, tambahkan panggilan ke ComputeFibonacci metode . Ambil parameter pertama untuk ComputeFibonacci dari Argument properti .DoWorkEventArgs Parameter BackgroundWorker dan DoWorkEventArgs akan digunakan nanti untuk pelaporan kemajuan dan dukungan pembatalan. Tetapkan nilai pengembalian dari ComputeFibonacci ke Result properti .DoWorkEventArgs Hasil ini akan tersedia untuk penanganan RunWorkerCompleted aktivitas.

    Catatan

    Penanganan DoWork aktivitas tidak mereferensikan backgroundWorker1 variabel instans secara langsung, karena ini akan menggabungkan penanganan aktivitas ini ke instans tertentu dari BackgroundWorker. Sebagai gantinya, referensi ke BackgroundWorker yang menaikkan peristiwa ini dipulihkan dari sender parameter . Ini penting ketika formulir menghosting lebih dari satu BackgroundWorker. Penting juga untuk tidak memanipulasi objek antarmuka pengguna apa pun di penanganan aktivitas Anda DoWork . Sebagai gantinya, komunikasikan ke antarmuka pengguna melalui BackgroundWorker peristiwa.

    // This event handler is where the actual,
    // potentially time-consuming work is done.
    void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
    {
       // Get the BackgroundWorker that raised this event.
       BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    
       // Assign the result of the computation
       // to the Result property of the DoWorkEventArgs
       // object. This is will be available to the 
       // RunWorkerCompleted eventhandler.
       e->Result = ComputeFibonacci( safe_cast<Int32>(e->Argument), worker, e );
    }
    
    // This event handler is where the actual,
    // potentially time-consuming work is done.
    private void backgroundWorker1_DoWork(object sender,
        DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;
    
        // Assign the result of the computation
        // to the Result property of the DoWorkEventArgs
        // object. This is will be available to the
        // RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci((int)e.Argument, worker, e);
    }
    
    ' This event handler is where the actual work is done.
    Private Sub backgroundWorker1_DoWork( _
    ByVal sender As Object, _
    ByVal e As DoWorkEventArgs) _
    Handles backgroundWorker1.DoWork
    
        ' Get the BackgroundWorker object that raised this event.
        Dim worker As BackgroundWorker = _
            CType(sender, BackgroundWorker)
    
        ' Assign the result of the computation
        ' to the Result property of the DoWorkEventArgs
        ' object. This is will be available to the 
        ' RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci(e.Argument, worker, e)
    End Sub
    
  4. Di penanganan startAsyncButton aktivitas kontrol Click , tambahkan kode yang memulai operasi asinkron.

    void startAsyncButton_Click( System::Object^ /*sender*/, System::EventArgs^ /*e*/ )
    {
       
       // Reset the text in the result label.
       resultLabel->Text = String::Empty;
    
       // Disable the UpDown control until 
       // the asynchronous operation is done.
       this->numericUpDown1->Enabled = false;
    
       // Disable the Start button until 
       // the asynchronous operation is done.
       this->startAsyncButton->Enabled = false;
    
       // Enable the Cancel button while 
       // the asynchronous operation runs.
       this->cancelAsyncButton->Enabled = true;
    
       // Get the value from the UpDown control.
       numberToCompute = (int)numericUpDown1->Value;
    
       // Reset the variable for percentage tracking.
       highestPercentageReached = 0;
    
       // Start the asynchronous operation.
       backgroundWorker1->RunWorkerAsync( numberToCompute );
    }
    
    private void startAsyncButton_Click(System.Object sender,
        System.EventArgs e)
    {
        // Reset the text in the result label.
        resultLabel.Text = String.Empty;
    
        // Disable the UpDown control until
        // the asynchronous operation is done.
        this.numericUpDown1.Enabled = false;
    
        // Disable the Start button until
        // the asynchronous operation is done.
        this.startAsyncButton.Enabled = false;
    
        // Enable the Cancel button while
        // the asynchronous operation runs.
        this.cancelAsyncButton.Enabled = true;
    
        // Get the value from the UpDown control.
        numberToCompute = (int)numericUpDown1.Value;
    
        // Reset the variable for percentage tracking.
        highestPercentageReached = 0;
    
        // Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute);
    }
    
    Private Sub startAsyncButton_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles startAsyncButton.Click
    
        ' Reset the text in the result label.
        resultLabel.Text = [String].Empty
    
        ' Disable the UpDown control until 
        ' the asynchronous operation is done.
        Me.numericUpDown1.Enabled = False
    
        ' Disable the Start button until 
        ' the asynchronous operation is done.
        Me.startAsyncButton.Enabled = False
    
        ' Enable the Cancel button while 
        ' the asynchronous operation runs.
        Me.cancelAsyncButton.Enabled = True
    
        ' Get the value from the UpDown control.
        numberToCompute = CInt(numericUpDown1.Value)
    
        ' Reset the variable for percentage tracking.
        highestPercentageReached = 0
    
    
        ' Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute)
    End Sub 
    
  5. Di penanganan RunWorkerCompleted aktivitas, tetapkan hasil perhitungan ke resultLabel kontrol.

    // This event handler deals with the results of the
    // background operation.
    void backgroundWorker1_RunWorkerCompleted( Object^ /*sender*/, RunWorkerCompletedEventArgs^ e )
    {
       // First, handle the case where an exception was thrown.
       if ( e->Error != nullptr )
       {
          MessageBox::Show( e->Error->Message );
       }
       else
       if ( e->Cancelled )
       {
          // Next, handle the case where the user cancelled 
          // the operation.
          // Note that due to a race condition in 
          // the DoWork event handler, the Cancelled
          // flag may not have been set, even though
          // CancelAsync was called.
          resultLabel->Text = "Cancelled";
       }
       else
       {
          // Finally, handle the case where the operation 
          // succeeded.
          resultLabel->Text = e->Result->ToString();
       }
    
       // Enable the UpDown control.
       this->numericUpDown1->Enabled = true;
    
       // Enable the Start button.
       startAsyncButton->Enabled = true;
    
       // Disable the Cancel button.
       cancelAsyncButton->Enabled = false;
    }
    
    // This event handler deals with the results of the
    // background operation.
    private void backgroundWorker1_RunWorkerCompleted(
        object sender, RunWorkerCompletedEventArgs e)
    {
        // First, handle the case where an exception was thrown.
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else if (e.Cancelled)
        {
            // Next, handle the case where the user canceled
            // the operation.
            // Note that due to a race condition in
            // the DoWork event handler, the Cancelled
            // flag may not have been set, even though
            // CancelAsync was called.
            resultLabel.Text = "Canceled";
        }
        else
        {
            // Finally, handle the case where the operation
            // succeeded.
            resultLabel.Text = e.Result.ToString();
        }
    
        // Enable the UpDown control.
        this.numericUpDown1.Enabled = true;
    
        // Enable the Start button.
        startAsyncButton.Enabled = true;
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    ' This event handler deals with the results of the
    ' background operation.
    Private Sub backgroundWorker1_RunWorkerCompleted( _
    ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
    Handles backgroundWorker1.RunWorkerCompleted
    
        ' First, handle the case where an exception was thrown.
        If (e.Error IsNot Nothing) Then
            MessageBox.Show(e.Error.Message)
        ElseIf e.Cancelled Then
            ' Next, handle the case where the user canceled the 
            ' operation.
            ' Note that due to a race condition in 
            ' the DoWork event handler, the Cancelled
            ' flag may not have been set, even though
            ' CancelAsync was called.
            resultLabel.Text = "Canceled"
        Else
            ' Finally, handle the case where the operation succeeded.
            resultLabel.Text = e.Result.ToString()
        End If
    
        ' Enable the UpDown control.
        Me.numericUpDown1.Enabled = True
    
        ' Enable the Start button.
        startAsyncButton.Enabled = True
    
        ' Disable the Cancel button.
        cancelAsyncButton.Enabled = False
    End Sub
    

Menambahkan Pelaporan Kemajuan dan Dukungan untuk Pembatalan

Untuk operasi asinkron yang akan memakan waktu lama, sering kali diinginkan untuk melaporkan kemajuan kepada pengguna dan untuk memungkinkan pengguna membatalkan operasi. Kelas ini BackgroundWorker menyediakan peristiwa yang memungkinkan Anda untuk memposting kemajuan saat operasi latar belakang Anda berlanjut. Ini juga menyediakan bendera yang memungkinkan kode pekerja Anda mendeteksi panggilan ke CancelAsync dan mengganggu dirinya sendiri.

Menerapkan pelaporan kemajuan

  1. Di jendela Properti, pilih backgroundWorker1. Atur WorkerReportsProgress properti dan WorkerSupportsCancellation ke true.

  2. Deklarasikan dua variabel dalam FibonacciCalculator formulir. Ini akan digunakan untuk melacak kemajuan.

    int numberToCompute;
    int highestPercentageReached;
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    Private numberToCompute As Integer = 0
    Private highestPercentageReached As Integer = 0
    
  3. Tambahkan penanganan aktivitas untuk peristiwa tersebut ProgressChanged . Di penanganan ProgressChanged aktivitas, perbarui ProgressBar dengan ProgressPercentage properti ProgressChangedEventArgs parameter .

    // 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.Value = e.ProgressPercentage;
    }
    
    ' 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
    

Menerapkan dukungan untuk pembatalan

  1. Di penanganan cancelAsyncButton aktivitas kontrol Click , tambahkan kode yang membatalkan operasi asinkron.

    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(System.Object sender,
        System.EventArgs e)
    {
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    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
    
  2. Fragmen kode berikut dalam ComputeFibonacci metode melaporkan kemajuan dan mendukung pembatalan.

    if ( worker->CancellationPending )
    {
       e->Cancel = true;
    }
    
    if (worker.CancellationPending)
    {
        e.Cancel = true;
    }
    
    If worker.CancellationPending Then
        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 );
    }
    
    // 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);
    }
    
    ' 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
    

Pos pemeriksaan

Pada titik ini, Anda dapat mengkompilasi dan menjalankan aplikasi Kalkulator Fibonacci.

Menekan F5 untuk mengompilasikan dan menjalankan aplikasi.

Saat perhitungan berjalan di latar belakang, Anda akan melihat ProgressBar menampilkan kemajuan perhitungan terhadap penyelesaian. Anda juga dapat membatalkan operasi yang tertunda.

Untuk jumlah kecil, perhitungannya harus sangat cepat, tetapi untuk jumlah yang lebih besar, Anda akan melihat penundaan yang terlihat. Jika Anda memasukkan nilai 30 atau lebih besar, Anda akan melihat penundaan beberapa detik, tergantung pada kecepatan komputer Anda. Untuk nilai yang lebih besar dari 40, mungkin perlu waktu beberapa menit atau jam untuk menyelesaikan perhitungan. Meskipun kalkulator sibuk menghitung jumlah Fibonacci besar, perhatikan bahwa Anda dapat dengan bebas memindahkan formulir, meminimalkan, memaksimalkan, dan bahkan menutupnya. Ini karena utas UI utama tidak menunggu penghitungan selesai.

Langkah berikutnya

Sekarang setelah Anda menerapkan formulir yang menggunakan BackgroundWorker komponen untuk menjalankan komputasi di latar belakang, Anda dapat menjelajahi kemungkinan lain untuk operasi asinkron:

  • Gunakan beberapa BackgroundWorker objek untuk beberapa operasi simultan.

  • Untuk men-debug aplikasi multithread Anda, lihat Cara: Menggunakan Jendela Utas.

  • Terapkan komponen Anda sendiri yang mendukung model pemrograman asinkron. Untuk informasi selengkapnya, lihat Gambaran Umum Pola Asinkron berbasis peristiwa.

    Perhatian

    Saat menggunakan multithreading dalam bentuk apa pun, Anda berpotensi mengekspos diri Anda ke bug yang sangat serius dan kompleks. Lihat Praktik Terbaik Utas Terkelola sebelum menerapkan solusi apa pun yang menggunakan multithreading.

Lihat juga