四核的CPU普及了,.NET 4.0来了,并行编程似乎变得越来越普遍了。在最近的一个项目里,我尝试着引入了.NET 4.0在并行处理方面提供的一些新的技术,使代码更加简洁,也更易维护了。
在典型的后台查询、前台显示查询进度的对话框设计上,依照以往的方式,我通常是利用delegate.BeginInvoke()方法,通过预定的Callback方法实现UI更新。而使用BackgroundWorker这个类,实现了基于事件的异步模式之后,这项工作变得轻松了许多。
以下是我用Winform实现的一个简单示例,尽可能地保证了显示进度对话框的封闭性和代码简洁性。
源代码下载(7zip压缩)
一、显示进度对话框ProgressDlg的实现
public partial class ProgressDlg : Form { public ProgressDlg() { InitializeComponent(); } public void OnProgressChanged(object sender, ProgressChangedEventArgs e) { // show the progress. this.progressBar.Value = e.ProgressPercentage; } public void OnCompleted(object sender, RunWorkerCompletedEventArgs e) { // thread is completed, close the dialog. this.progressBar.Value = 100; this.Close(); } public CancelEventHandler OnCancel = null; private void btnCancel_Click(object sender, EventArgs e) { // thread is cancelled, close the dialog. this.Close(); } private void ProgressDlg_FormClosed(object sender, FormClosedEventArgs e) { // cast notification of cancellation. if (this.OnCancel != null) this.OnCancel(this, new CancelEventArgs()); } }
二、主窗口MainForm的实现
public partial class MainForm : Form { public MainForm() { InitializeComponent(); this._backworker = new BackgroundWorker(); // it's neccessary to set the properties. this._backworker.WorkerReportsProgress = true; this._backworker.WorkerSupportsCancellation = true; this._backworker.DoWork += new DoWorkEventHandler(backworker_DoWork); this._backworker.ProgressChanged += new ProgressChangedEventHandler(backworker_ProgressChanged); this._backworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backworker_RunWorkerCompleted); } private BackgroundWorker _backworker = null; // body of thread void backworker_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; i++) { Thread.Sleep(100); // trigger the event: ProgressChanged this._backworker.ReportProgress(i); // if receive the notification of Cancel. if (this._backworker.CancellationPending) { e.Cancel = true; return; } } } void backworker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // to do nothing. } void backworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // if thread is completed because of cancellation. if (e.Cancelled) this.labelSummary.Text = "Calculation is cancelled"; this.btnCalculate.Enabled = true; } void backworker_OnCancel(object sender, CancelEventArgs e) { this._backworker.CancelAsync(); } private void btnCalculate_Click(object sender, EventArgs e) { string temp = this.txtMax.Text.Trim(); if (string.IsNullOrEmpty(temp)) MessageBox.Show("Please input a valid integer.", "Validation", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); else { ProgressDlg progressDlg = new ProgressDlg(); // if thread is executing, dialog will show the progress. this._backworker.ProgressChanged += new ProgressChangedEventHandler(progressDlg.OnProgressChanged); this._backworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(progressDlg.OnCompleted); // if cancel in progressDlg, let me know and do something. progressDlg.OnCancel += new CancelEventHandler(backworker_OnCancel); this.btnCalculate.Enabled = false; progressDlg.Show(); // start the thread. this._backworker.RunWorkerAsync(); } } }