文章目录
参考资料:《C# 语言程序设计(第二版)》(唐大仕)
在学习使用 BackgroundWorker 来处理线程性的任务的时候,出现了两个bug,一直对比书中的代码,发现并没有敲错代码。最后还是老老实实看报错信息来尝试解决问题,试了一下还真解决了。特此分享一下自己的解决方案。
0. BackgroundWorker 的 3 个事件和 2 个方法
[1] 3 个事件
- DoWork事件:要进行的主要任务;
- RunWorkerCompleted事件:任务完成后要做的事;
- ProgressChanged 事件:当进度改变时要做的事。
[2] 2 个方法
- RunWorkerAsync():来开始任务的执行,从而激发 DoWork 事件的开始执行;
- ReportProgress():来报告进度的改变,从而激发 ProgressChanged 事情的执行。
1. BackgroundWorker 声明它不报告进度
[1] 报错信息:
[2] 分析
通过报错信息可以轻松发现,BackgroundWorker 中有一个属性叫做 ”WorkerReportsProgress“,是用来声明是否报告进度的。
所以我们需要在调用 BackgroundWorker 的 RunWorkerAsync() 方法之前显示声明这个属性为 true,表示要报告进度,这样就没有问题了。
PS:上面说 BackgroundWorker有两个方法,分别是:
- RunWorkerAsync()
- ReportProgress()
那这里为什么是在 RunWorkerAsync() 方法之前添加 WorkerReportsProgress = true 而不是在 ReportProgress() 方法呢?
个人理解是:当前项目执行的主要任务就涉及到了进度的变化,自然而然会调用到 ReportProgress() 方法,所以要在启动任务执行的 RunWorkerAsync() 方法之前添加这个声明。
[3] 修改代码
在启动任务执行的地方添加
backgroundWorker1.WorkerReportsProgress = true;
2. BackgroundWorker 声明它不支持取消
[1] 报错信息
其实这个报错信息跟上面一个是同一个道理的,但是这里涉及到的属性是WorkerSupporstCancellation。原理相同,不赘述。
[2] 修改代码
3. 项目源码
[0] 项目说明
使用 BackgroundWorker 计算裴波那契数。并在界面中不断更新进度。
[1] 界面设计
[2] 代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace BackgroudWorkerTest
{
public partial class Form1 : Form
{
private int numberToCompute = 1;
private int highestPercentageReached = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
InitializeBackgroundWorker();
}
public void InitializeBackgroundWorker()
{
//注意各个事件
backgroundWorker1.DoWork += new DoWorkEventHandler(
backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}
//启动按钮
private void startAsyncButton_Click(object sender, EventArgs e)
{
resultLabel1.Text = String.Empty;
//设置空间的状态
this.numericUpDown1.Enabled = false;
this.startAsyncButton.Enabled = false;
this.cancelAsyncButton.Enabled = true;
//得到要计算的数
numberToCompute = (int)numericUpDown1.Value;
//进度值
highestPercentageReached = 0;
backgroundWorker1.WorkerReportsProgress = true;
//启动任务的执行
backgroundWorker1.RunWorkerAsync(numberToCompute);
}
//取消按钮
private void cancelAsyncButton_Click(object sender, EventArgs e)
{
//处理取消
backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.CancelAsync();
cancelAsyncButton.Enabled = false;
}
//BackgroundWorker要进行的主要任务
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = ComputeFibonacci((int)e.Argument, worker, e);
}
//任务完成后要做的事
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//任务完成,如果没有错误,则显示结果
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
resultLabel1.Text = "Cancelled";
}
else
{
resultLabel1.Text = e.Result.ToString();
}
this.numericUpDown1.Enabled = true;
startAsyncButton.Enabled = true;
cancelAsyncButton.Enabled = false;
}
//进度改变时要做的事情
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
//使用进度条显示进度
this.progressBar1.Value = e.ProgressPercentage;
}
//计算斐波那契数
long ComputeFibonacci(int n,BackgroundWorker worker,DoWorkEventArgs e)
{
if(n<0 || n > 91)
{
throw new ArgumentException(
"value must be >=0 and <= 91", "n");
}
long result = 0;
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
if (n < 2)
{
result = 1;
}
else
{
//使用递归进行计算
result = ComputeFibonacci(n - 1, worker, e) +
ComputeFibonacci(n - 2, worker, e);
}
//报告进度的百分数
int percentComplete =
(int)((float)n / (float)numberToCompute * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
worker.ReportProgress(percentComplete);
}
}
return result;
}
}
}
[3] 效果演示
以上为个人学习过程中的一点总结,如有错误,还望批评指正!感激不尽!
Over~