BackgroundWorker 实现多线程操作
背景介绍:
在做程序的过程中,我们很可能遇到这样的情况:当我们执行一个比较耗时的操作,即界面加载数据量略大的时,在该操作未完成之前再去操作界面,就会出现停止响应的情况,这称为界面假死状态,那一个小圆圈转呀转的,想必大家看着就头疼。当然这是一个非常影响用户体验度的地方。
怎么做出一个能够及时响应的用户界面呢?多线程操作。
引入BackgroundWorker组件:
BackgroundWorker是·net里用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作。
常用方法
1.RunWorkerAsync 开始执行后台操作。引发 DoWork 事件
2.CancelAsync 请求取消挂起的后台操作。
注意:这个方法是将 CancellationPending 属性设置为 true,并不会终止后台操作。在后台操作中要检查 CancellationPending 属性,来决定是否要继续执行耗时的操作。
3.ReportProgress 引发 ProgressChanged 事件。
常用属性
1.CancellationPending 指示应用程序是否已请求取消后台操作。只读属性,默认为 false,当执行了 CancelAsync 方法后,值为 true。
2.WorkerSupportsCancellation 指示是否支持异步取消。要执行 CancelAsync 方法,需要先设置该属性为 true。
3.WorkerReportsProgress 指示是否能报告进度。要执行 ReportProgress 方法,需要先设置该属性为 true。
常用事件
1.DoWork 调用 RunWorkerAsync 方法时发生。
2.RunWorkerCompleted 后台操作已完成、被取消或引发异常时发生。
3.ProgressChanged 调用 ReportProgress 方法时发生。
注意:在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和RunWorkerCompleted 事件与用户界面进行通信。
如果想在 DoWork 事件处理程序中和用户界面的控件通信,可在用 ReportProgress 方法。ReportProgress(int percentProgress, object userState),可以传递一个对象。
ProgressChanged 事件可以从参数ProgressChangedEventArgs 类的UserState 属性得到这个信息对象。这个事件也可以实现进度条功能,把任务的进度实时呈现给用户。
简单的程序用BackgroundWorker 比 Thread 方便,Thread中和用户界面上的控件通信比较麻烦,需要用委托来调用控件的 Invoke 或BeginInvoke 方法,没有 BackgroundWorker 方便。
BackgroundWorker Demo
namespace BackgroundWorkerTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); backgroundWorker1.WorkerReportsProgress = true;//报告完成进度 backgroundWorker1.WorkerSupportsCancellation = true;//允许用户终止后台线程 } //开始按钮 private void button1_Click(object sender, EventArgs e) { if (!backgroundWorker1.IsBusy )//判断backgroundWorker1是否正在运行异步操作 { //backgroundWorker1.RunWorkerAsync(); backgroundWorker1.RunWorkerAsync(1000);//开始执行后台操作,调用DoWork事件 } } //DoWork事件声明要执行的耗时操作 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { e.Result = ListNumber(backgroundWorker1, e);//运算结果保存在e.Result中 } bool ListNumber(object sender, DoWorkEventArgs e) { int num=(int)e.Argument;//接收传入的参数 for (int i = 1; i <= num; i++) { if (backgroundWorker1.CancellationPending)//判断是否请求了取消后台操作 { e.Cancel=true; return false; } //backgroundWorker1.ReportProgress(i * 100 / num); backgroundWorker1.ReportProgress(i * 100 / num,i);//报告完成进度 Thread.Sleep(0);//后台线程交出时间片 } return true; } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { //将完成进度数据传给进度条 progressBar1.Value = e.ProgressPercentage; label1.Text = e.ProgressPercentage + "%"; //将中间计算结果在ListBox控件中显示出来 listBox1.Items.Add(e.UserState); } Private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (!e.Cancelled && e.Error==null) { MessageBox.Show("处理完成了吗? " + e.Result); } else//如果取消后台线程或发生了异常 { MessageBox.Show("处理完成了吗? false"); } } //取消按钮 private void button2_Click(object sender, EventArgs e) { if (backgroundWorker1.WorkerSupportsCancellation==true) { backgroundWorker1.CancelAsync();//取消后台操作 backgroundWorker1.Dispose();//释放资源 } } } }
总结:
使用backgroundWorker实现多线程大致的步骤是:
1、 绑定线程,设置属性
2、调用BackgroundWorker的RunWorkerAsync方法(可以传递参数),它将调用DoWork事件
3、声明DoWork事件的委托方法,在后台执行耗时的操作
4、在耗时操作中判断CancellationPending属性,如果为false则退出
5、如果要向用户界面发送信息,则调用BackgroundWorker的ReportProgress方法,它将调用ProgressChanged事件(可以将改变通过object类型传递)
6、在ProgressChanged事件的响应代码中将改变呈现给用户,类似进度条。
7、如果需要取消耗时操作,则调用BackgroundWorker的CancelAsync方法,需要和步骤3一起使用
总的来说就是用backgroundWorker组件来新建一个线程,把耗时的部分放到这个线程中在后台进行处理。这样就不会影响界面的正常使用。举个通俗的例子,在我们打开一个网页的时候,先加载完的总是文字,然后图片在慢慢出现。这就是线程的应用,网页打开的时候先呈现出文字,供用户浏览,然后把加载图片放到一个单独的线程中,异步的在后台执行,执行完毕后把图片呈现出来。
这样就避免了一打开界面就加载大量信息,而造成的界面假死状态,大大提高了用户体验度。
线程处理这块不熟,希望大家指点。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。