C# backgroundworker类
概念
在C#程序中,经常会有一些耗时较长的CPU密集型运算,如果直接在 UI 线程执行这样的运算就会出现UI不响应的问题。解决这类问题的主要途径是使用多线程,启动一个后台线程,把运算操作放在这个后台线程中完成。但是原生接口的线程操作有一些难度,如果要更进一步的去完成线程间的通信就会难上加难。
C#的BackgroundWorker类是一个用于简化在桌面应用程序(如Windows Forms或WPF)中执行后台任务的工具。它的主要目的是在不阻塞用户界面(UI)的情况下运行耗时操作,同时支持进度报告和任务取消。
主要作用
后台执行耗时任务
在单独的线程中运行长时间操作(如文件下载、数据处理),防止UI线程被阻塞,保持界面响应。
进度更新
允许在后台任务中报告进度(如更新进度条),并自动将更新操作同步到UI线程。
取消支持
提供机制让用户请求取消正在执行的后台任务。
完成通知
在任务完成(成功、取消或出错)后,触发事件以更新UI或处理结果。
关键成员
成员 | 类型 | 说明 |
---|---|---|
DoWork | 事件 | 后台任务逻辑:在此事件处理程序中编写耗时操作代码。此代码在后台线程执行。 |
ProgressChanged | 事件 | 进度更新:通过ReportProgress() 方法触发,用于在UI上更新进度(如进度条)。 |
RunWorkerCompleted | 事件 | 任务完成通知:在任务结束时触发,处理结果或错误。 |
RunWorkerAsync() | 方法 | 启动后台任务。 |
CancelAsync() | 方法 | 请求取消任务(需设置WorkerSupportsCancellation = true )。 |
WorkerReportsProgress | 属性 | 设置为true 以启用进度报告功能。 |
WorkerSupportsCancellation | 属性 | 设置为true 以允许任务取消。 |
典型使用场景
// 创建BackgroundWorker实例
var worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
// 订阅事件
worker.DoWork += (sender, e) =>
{
for (int i = 0; i <= 100; i++)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
// 模拟耗时操作并报告进度
worker.ReportProgress(i);
Thread.Sleep(50);
}
};
worker.ProgressChanged += (sender, e) =>
{
// 更新UI进度条(自动在UI线程执行)
progressBar.Value = e.ProgressPercentage;
};
worker.RunWorkerCompleted += (sender, e) =>
{
if (e.Cancelled) MessageBox.Show("任务已取消!");
else if (e.Error != null) MessageBox.Show($"错误:{e.Error.Message}");
else MessageBox.Show("任务完成!");
};
// 启动任务
worker.RunWorkerAsync();
注意事项
-
跨线程访问UI
-
DoWork
事件在后台线程运行,禁止直接操作UI控件。 -
ProgressChanged
和RunWorkerCompleted
事件会自动在UI线程触发,可直接更新UI。
-
-
替代方案
现代C#更推荐使用async/await
配合Task.Run
处理异步操作,代码更简洁。但BackgroundWorker
仍适用于旧项目或需要深度集成进度/取消逻辑的场景。
与async/await对比
特性 | BackgroundWorker | async/await + Task.Run |
---|---|---|
复杂度 | 基于事件,代码分散 | 线性结构,代码更清晰 |
取消支持 | 内置CancellationPending 机制 | 通过CancellationToken 实现 |
进度报告 | 内置ReportProgress | 需自定义(如IProgress<T> 接口) |
适用场景 | 简单后台任务 | 复杂异步操作,需更高灵活性 |
总结:BackgroundWorker
是简化桌面应用后台任务处理的经典工具,适合需要快速实现进度更新和取消功能的场景。但在新项目中,可优先考虑async/await
以获得更现代的编程体验。
实例(源码)
public partial class Form2 : Form
{
public BackgroundWorker worker = new BackgroundWorker();
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
// Worker 工人
// Reports 报告
// Progress 进度
// WorkeyRepoetsProgress 是否支持BackgroundWorker报告异步任务进度的功能。
worker.WorkerReportsProgress = true;
// WorkerSupportsCancellation 是否支持异步取消操作
worker.WorkerSupportsCancellation = true;
// 三个事件
// 1 DoWork: 做异步耗时的操作(调用RunWorkerAsync方法之后触发)
worker.DoWork += Worker_DoWork;
// 2 ProgressChange 进度发生变化的事件,WorkerReportsProgress属性true的时候触发
worker.ProgressChanged += Worker_ProgressChanged;
// 3 RWorkerCompleted 后台操作完成的时候触发,取消或者异常的时候都会触发
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
}
//做异步任务的函数
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
//模拟处理超时的操作
Random rnd = new Random();
for (int i = 0; i < 80; i++)
{
if (worker.CancellationPending) //判断用户是否取消了后台任务
{
e.Cancel = true; //取消后台任务
return;
}
else
{
//耗时任务(文件读写,通讯、加载tb块)
Thread.Sleep(rnd.Next(100, 1000)); //休眠100-1000ms之间,模拟耗时的
//报告进度
int progress = (int)Math.Round(((i + 1) / 80.0) * 100);
progress = progress > 100 ? 100 : progress;
worker.ReportProgress(progress);// 触发进度变化的事件
}
}
}
//加载进度的函数
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;// 把ReportProgress(progress)函数里面的参数传递过来,取出
}
//后台操作处理完的函数
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.BackColor = Color.Red;// 后台任务执行完之后的操作
}
//开始异步任务的按钮
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine(worker.IsBusy);// backWrork是否是繁忙状态
if (!worker.IsBusy) // 空闲状态下
{
worker.RunWorkerAsync();// 开始后台任务
}
}
//取消异步任务的按钮
private void button2_Click(object sender, EventArgs e)
{
if (worker.IsBusy) //非空闲状态下取消
{
worker.CancelAsync();//取消后台任务
}
}
}