前言:

C#的应用软件中,经常要考虑到UI的相应和处理的程序(尤其占用时间很长的程序)之前的相应配合问题。

传统的思路,用线程的控制方法,加原子锁等方法,可是,再怎么搞都没有 windows C#自带的 BackgroundWorker 的方法更高效和稳定。

因为,`BackgroundWorker` 组件是实现异步编程的一种方式,它允许开发者在不阻塞主线程的情况下执行耗时操作,并通过事件与主线程安全地交互。
这个是windows内部,基于操作系统的工具。

在下面的参考代码里面,我们做了一个简单的带进度条的后台进程程序。


BackgroundWorker 的基本概念:

在C#中,BackgroundWorker 是一个组件,用于在后台线程中执行长时间运行的任务,而不会影响用户界面的响应性。BackgroundWorker 允许你在后台线程中执行操作,同时可以安全地与主线程(通常是UI线程)通信。

1.2 属性和方法:

 属性:

- `CancellationPending`: 一个只读属性,用于指示是否已经请求取消后台操作。如果用户请求取消操作,此属性将返回 `true`。

- `IsBusy`: 指示 `BackgroundWorker` 是否正在执行操作。

- `WorkerReportsProgress`: 用于标识,你是设置或获取 `BackgroundWorker` 报告进度更改。

- `WorkerSupportsCancellation`: 用于设置或获取 `BackgroundWorker` 是否支持取消操作。

事件:

- `DoWork`: 当 `RunWorkerAsync` 方法被调用时,此事件被触发。通常在此事件的处理程序中执行后台任务。

- `ProgressChanged`: 当 `ReportProgress` 方法被调用时,此事件被触发。可以在事件处理程序中更新用户界面以反映进度。

- `RunWorkerCompleted`: 当后台操作完成时,无论是正常完成、被取消还是发生错误,此事件都会被触发。

方法:(public)

- `CancelAsync`: 请求取消后台操作。如`WorkerSupportsCancellation` 属性设置为 `true`,此方法将触发 `CancellationPending` 属性变为 `true`。

- `ReportProgress`: 报告后台操作的进度。如 `WorkerReportsProgress` 属性设置为 `true`,可以使用此方法来更新进度指示器或执行其他与进度相关的操作。

- `RunWorkerAsync`: 启动后台操作

- `RunWorkerAsync (object argument)`: 带有参数的 `RunWorkerAsync` 方法。

受保护的虚方法:(只能在本背景类里面被调用,且可以重写和拓展)(protected)

- `OnDoWork(DoWorkEventArgs e)`: 当 `DoWork` 事件被触发时,此方法被调用。通常在此方法中实现后台任务的逻辑。

- `OnProgressChanged(ProgressChangedEventArgs e)`: 当 `ProgressChanged` 事件被触发时,此方法被调用。可以在此处更新进度信息。

- `OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)`: 当 `RunWorkerCompleted` 事件被触发时,此方法被调用。可以在此处处理后台操作完成后的清理工作或结果。

 BackgroundWorker 具有的属性和方法如上,大多数是字面意思,我们读的懂,就可以用,我后面列举两个例子:


2 构建 BackgroundWorker 的方法:

2.1 加载


菜单: 视图\工具箱\组件\ 里面,可以找到BackgroundWorker ,然后,直接拖拽到你的主FROM

在主窗口的下沿:会出现定义。

2.2 属性

WorkerReportsProgress(如果需要报告进度)和 WorkerSupportsCancellation(如果需要支持取消操作)


3 使用方法 

3.3 背景Action的定义

3.3.1 DoWork方法

双击会自动进入背景处理程序DoWork方法:在这里写你的背景动作。

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            // 在这里编写长时间运行的任务


        }

 当然,你双击的时候,绑定是自动的

            this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);


3.4 过程进度和进度条的实现:

3.4.1 加入StatusStrip 状态栏:

3.4.2 加入进度条:

 3.4.3 绑定到背景程序:

我们先看一下前面,1.2的属性和方法,和进度相关的有两个,一个是事件一个是报告进度,

- `WorkerReportsProgress`: 用于设置或获取 `BackgroundWorker` 是否报告进度更改。

- `ReportProgress`: 报告后台操作的进度。如 `WorkerReportsProgress` 属性设置为 `true`,可以使用此方法来更新进度指示器或执行其他与进度相关的操作。

- `OnProgressChanged(ProgressChangedEventArgs e)`: 当 `ProgressChanged` 事件被触发时,此方法被调用。可以在此处更新进度信息。 

 在绑定之前,我们先搞清楚,这3个接口的意义,

WorkerReportsProgress,用来表示是否需要报告进度。ReportProgress 方法(被调用的时候,当然,一般是在(背景任务中)DoWork中被调用。由于,WorkerReportsProgress = ture, ReportProgress 调用的时候会触发事件,OnProgressChanged,并把ReportProgress输入的进度的数值0到100,通过事件EvengArgs给到事件处理函数ProgressChanged。


3.5 启动任务

启动 BackgroundWorker的方法, RunWorkerAsync ,有时候可以带参数,object argument,object argument 参数非常有用,因为它允许你将数据传递给后台工作线程。

private void btnStart_Click(object sender, EventArgs e)
{
    // 启动后台工作
    backgroundWorker1.RunWorkerAsync();
}

3.6 完成任务事件

RunWorkerCompleted事件在后台程序完成或者取消的时候,会被触发

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // 检查后台工作是否成功完成
    if (e.Error != null)
    {
        // 处理错误
        MessageBox.Show("发生错误: " + e.Error.Message);
    }
    else if (e.Cancelled)
    {
        // 处理取消操作
        MessageBox.Show("操作已取消。");
    }
    else
    {
        // 使用 e.Result 获取 DoWork 事件处理器的结果
        MessageBox.Show("操作完成: " + e.Result);
    }
}

3.7 后台的取消:


3.7.1 CancellationPending 属性

指示是否已经请求取消后台操作

3.7.2 WorkerSupportsCancellation 属性

取消后台进程是否使能

3.7.3 请求取消后台方法:CancelAsync

WorkerSupportsCancellation  == true,时候,将触发CancellationPending = true


4 参考代码:

在下面的参考代码里面,我们做了一个简单的带进度条的后台进程程序。

4.0 初始化:

【作者案】BackgroundWorker ,上面的定义的属性、事件、方法和接口,对于public的定义,我们可以之间用"."来调用,而protected的方法,需要进行实例的初始化。而初始化的时候,EventArgs需要传递的是Handler,例如:ProgressChangedEventArgs,对应ProgressChangedEventHandler ,在初始化的时候也可以绑定起来。

在Form的InitializeComponent定义里面,我们可以如下定义:

         // 
            // backgroundWorker1
            // 
            this.backgroundWorker1.WorkerReportsProgress = true;
            this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
            this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);
            this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

4.1 启动后台

4.1.1 不带参数的启动

// 在窗体中,启动 BackgroundWorker 并传递文件路径
private void btnStartProcess_Click(object sender, EventArgs e)
{
    if(!backgroundWorker1.IsBusy){
        backgroundWorker1.RunWorkerAsync();
    }
}

 通过IsBusy先判断,是否已经启动了,避免重复启动。

4.1.2 带参数的启动

// 在窗体中,启动 BackgroundWorker 并传递文件路径
private void btnStartProcess_Click(object sender, EventArgs e)
{
    string filePath = "path/to/your/file.txt";
    backgroundWorker1.RunWorkerAsync(filePath);
}

【案】,注意: RunWorkerAsync的接口

4.2 后台执行任务

 4.2.1 模拟进度更新:

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            // 在这里编写长时间运行的任务
            BackgroundWorker worker = sender as BackgroundWorker;
            for (int i = 0; i <= 100; i++)
            {
                // 模拟工作
                Thread.Sleep(10); // 模拟耗时操作
                                  // 报告进度
                worker.ReportProgress(i); // 这里的 i 将作为 ProgressPercentage
            }

        }

4.2.2 模拟代入一个文件路径参数 

// 在 BackgroundWorker 的 DoWork 事件处理器中使用参数
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    string filePath = e.Argument as string; // 从参数中获取文件路径

    if (!worker.CancellationPending)
    {
        // 使用 worker 执行后台任务...
         if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
        {
        // 使用文件路径执行后台任务
        // 例如,读取文件内容并进行处理
        string fileContent = File.ReadAllText(filePath);
        // ... 对文件内容进行处理 ...

        e.Result = "处理完成"; // 可以将结果设置在 e.Result 中,稍后在 RunWorkerCompleted 事件中使用
        }
        else
        {
            e.Cancel = true; // 如果文件路径无效或文件不存在,取消操作
            worker.ReportProgress(0, "文件路径无效或文件不存在。");
         }

   }

   
}

 BackgroundWorker worker = sender as BackgroundWorker;

这行代码的目的是将事件的 sender 参数转换为 BackgroundWorker 类型,以便使用 BackgroundWorker 类的成员。如果转换成功,worker 将是一个指向原始 BackgroundWorker 实例的引用

string filePath = e.Argument as string; // 从参数中获取文件路径

4.3 后台完成

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        MessageBox.Show("操作已取消。");
    }
    else if (e.Error != null)
    {
        MessageBox.Show("发生错误: " + e.Error.Message);
    }
    else
    {
        // 使用 DoWork 事件处理器的结果
        MessageBox.Show("操作结果: " + e.Result);
    }
}

5 运行结果展示:

我在Form的底部加了一个进度条,然后,运行后台模拟进度后,显示如下:

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部