演练:在后台运行操作
如果某项操作需要很长时间才能完成,而你不希望造成用户界面的延迟,则可以使用 BackgroundWorker 类在另一个线程上运行此操作。
有关此示例中使用的代码的完整列表,请参阅如何:在后台运行操作。
在后台运行操作
当窗体在 Visual Studio 中的 Windows 窗体设计器中处于活动状态时,将两个 Button 控件从“工具箱”拖动到窗体中,然后根据下表设置按钮的
Name
和 Text 属性。Button 名称 文本 button1
startBtn
开始 button2
cancelBtn
Cancel 打开“工具箱”,单击“组件”选项卡,然后将 BackgroundWorker 组件拖动到窗体上。
backgroundWorker1
组件显示在“组件栏”中。在“属性” 窗口中,将 WorkerSupportsCancellation 属性设置为
true
。在“属性”窗口中,单击“事件”按钮,然后双击 DoWork 和 RunWorkerCompleted 事件以创建事件处理程序。
将耗时的代码插入 DoWork 事件处理程序。
从参数 DoWorkEventArgs 的 Argument 属性中提取操作所需的任何参数。
将计算结果分配给 DoWorkEventArgs 的 Result 属性。
此结果将可供 RunWorkerCompleted 事件处理程序使用。
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // Do not access the form's BackgroundWorker reference directly. // Instead, use the reference provided by the sender parameter. BackgroundWorker bw = sender as BackgroundWorker; // Extract the argument. int arg = (int)e.Argument; // Start the time-consuming operation. e.Result = TimeConsumingOperation(bw, arg); // If the operation was canceled by the user, // set the DoWorkEventArgs.Cancel property to true. if (bw.CancellationPending) { e.Cancel = true; } }
Private Sub backgroundWorker1_DoWork( _ sender As Object, e As DoWorkEventArgs) _ Handles backgroundWorker1.DoWork ' Do not access the form's BackgroundWorker reference directly. ' Instead, use the reference provided by the sender parameter. Dim bw As BackgroundWorker = CType( sender, BackgroundWorker ) ' Extract the argument. Dim arg As Integer = Fix(e.Argument) ' Start the time-consuming operation. e.Result = TimeConsumingOperation(bw, arg) ' If the operation was canceled by the user, ' set the DoWorkEventArgs.Cancel property to true. If bw.CancellationPending Then e.Cancel = True End If End Sub
在 RunWorkerCompleted 事件处理程序中插入用于检索操作结果的代码。
// This event handler demonstrates how to interpret // the outcome of the asynchronous operation implemented // in the DoWork event handler. private void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { // The user canceled the operation. MessageBox.Show("Operation was canceled"); } else if (e.Error != null) { // There was an error during the operation. string msg = String.Format("An error occurred: {0}", e.Error.Message); MessageBox.Show(msg); } else { // The operation completed normally. string msg = String.Format("Result = {0}", e.Result); MessageBox.Show(msg); } }
' This event handler demonstrates how to interpret ' the outcome of the asynchronous operation implemented ' in the DoWork event handler. Private Sub backgroundWorker1_RunWorkerCompleted( _ sender As Object, e As RunWorkerCompletedEventArgs) _ Handles backgroundWorker1.RunWorkerCompleted If e.Cancelled Then ' The user canceled the operation. MessageBox.Show("Operation was canceled") ElseIf (e.Error IsNot Nothing) Then ' There was an error during the operation. Dim msg As String = String.Format("An error occurred: {0}", e.Error.Message) MessageBox.Show(msg) Else ' The operation completed normally. Dim msg As String = String.Format("Result = {0}", e.Result) MessageBox.Show(msg) End If End Sub
实现
TimeConsumingOperation
方法。// This method models an operation that may take a long time // to run. It can be cancelled, it can raise an exception, // or it can exit normally and return a result. These outcomes // are chosen randomly. private int TimeConsumingOperation( BackgroundWorker bw, int sleepPeriod ) { int result = 0; Random rand = new Random(); while (!bw.CancellationPending) { bool exit = false; switch (rand.Next(3)) { // Raise an exception. case 0: { throw new Exception("An error condition occurred."); break; } // Sleep for the number of milliseconds // specified by the sleepPeriod parameter. case 1: { Thread.Sleep(sleepPeriod); break; } // Exit and return normally. case 2: { result = 23; exit = true; break; } default: { break; } } if( exit ) { break; } } return result; }
' This method models an operation that may take a long time ' to run. It can be cancelled, it can raise an exception, ' or it can exit normally and return a result. These outcomes ' are chosen randomly. Private Function TimeConsumingOperation( _ bw As BackgroundWorker, _ sleepPeriod As Integer) As Integer Dim result As Integer = 0 Dim rand As New Random() While Not bw.CancellationPending Dim [exit] As Boolean = False Select Case rand.Next(3) ' Raise an exception. Case 0 Throw New Exception("An error condition occurred.") Exit While ' Sleep for the number of milliseconds ' specified by the sleepPeriod parameter. Case 1 Thread.Sleep(sleepPeriod) Exit While ' Exit and return normally. Case 2 result = 23 [exit] = True Exit While Case Else Exit While End Select If [exit] Then Exit While End If End While Return result End Function
在 Windows 窗体设计器中,双击
startButton
以创建 Click 事件处理程序。调用
startButton
的 Click 事件处理程序中的 RunWorkerAsync 方法。private void startBtn_Click(object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(2000); }
Private Sub startButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles startBtn.Click Me.backgroundWorker1.RunWorkerAsync(2000) End Sub
在 Windows 窗体设计器中,双击
cancelButton
以创建 Click 事件处理程序。调用
cancelButton
的 Click 事件处理程序中的 CancelAsync 方法。private void cancelBtn_Click(object sender, EventArgs e) { this.backgroundWorker1.CancelAsync(); }
Private Sub cancelButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cancelBtn.Click Me.backgroundWorker1.CancelAsync() End Sub
在文件的顶部,导入 System.ComponentModel 和 System.Threading 命名空间。
using System; using System.ComponentModel; using System.Drawing; using System.Threading; using System.Windows.Forms;
Imports System.ComponentModel Imports System.Drawing Imports System.Threading Imports System.Windows.Forms
按 F6 生成解决方案,然后按 Ctrl+F5 在调试程序外部运行应用程序。
注意
如果按 F5 在调试程序下运行应用程序,则调试程序将捕获并显示
TimeConsumingOperation
方法中引发的异常。 在调试程序外部运行应用程序时,BackgroundWorker 将处理异常并将其缓存到 RunWorkerCompletedEventArgs 的 Error 属性中。单击“启动”按钮以运行异步操作,然后单击“取消”按钮以停止正在运行的异步操作。
每个操作的结果显示在 MessageBox 中。
后续步骤
实现在异步操作进行时报告进度的窗体。 有关详细信息,请参阅如何:实现使用后台操作的窗体。
实现一个支持组件异步模式的类。 有关详细信息,请参阅实现基于事件的异步模式。