在一些耗时的操作过程中,在长时间运行时可能会导致用户界面 (UI) 处于停止响应状态,用户在这操作期间无法进行其他的操作,为了不使UI层处于停止响应状态,我们倾向推荐用户使用BackgroundWorker来进行处理,这个后台的线程处理,可以很好的实现常规操作的同时,还可以及时通知UI,包括当前处理信息和进度等,这个BackgroundWorker的处理在百度里面也是有很多使用的介绍,本篇随笔主要是做一些自己的使用总结,希望也能给读者提供一个参考。

在使用BackgroundWorker的过程中,我们可以定义自己的状态参数信息,从而实现线程状态的实时跟踪以及进度和信息提示,方便我们及时通知UI进行更新。本篇随笔主要针对一些数据采集过程的处理,在网上采集特定的数据往往需要耗时几个小时以上,如果采用常规的同步操作,比较麻烦,而如果引入一些SmartThreadPool这些第三方类库有显得臃肿,而且资源耗费的也很严重,因此使用BackgroundWorker相对比较轻型的方案比较吸引我。

采集的数据处理

例如是我采集数据的一个局部界面,主要是根据一些参数进行数据的采集,采集过程可以通过状态栏和右边的标签进行反馈,在状态栏显示采集进度等信息,实现比较友好的信息显示。

一般我们定义后台线程处理,可以在该窗体定义一个变量即可,如下代码所示。

        private BackgroundWorker worker = new BackgroundWorker();

然后就是对这个后台线程处理对象的一些事件进行实现即可,如下代码所示

    public partial class MainFrame : BaseForm
{
/// <summary>
/// 增加一个变量来记录线程状态
/// </summary>
private bool IsThreadRunning = false;
private BackgroundWorker worker = new BackgroundWorker(); public MainFrame()
{
InitializeComponent(); Portal.gc.InitData(); worker.WorkerSupportsCancellation = true; //支持取消
worker.WorkerReportsProgress = true; //支持报告进度
worker.DoWork += worker_DoWork; //处理过程
worker.RunWorkerCompleted += worker_RunWorkerCompleted; //完成操作
worker.ProgressChanged += worker_ProgressChanged; //报告进度
}

例如进度条的通知,主要就是计算总任务的数量,以及当前完成的人数数量,我们实现代码如下所示

        /// <summary>
/// 进度条的通知
/// </summary>
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.barProgress.EditValue = e.ProgressPercentage;
CollectStateInfo stateInfo = e.UserState as CollectStateInfo;
if (stateInfo != null)
{
var message = string.Format("正在采集 {0} 的 {1} , 项目名称为:{2}", stateInfo.TotalRecords, stateInfo.CompletedRecord + , stateInfo.CurrentItemName);
this.lblTips.Text = message;
this.barTips.Caption = message; //记录运行位置
JobParameterHelper.SaveData(new CurrentJobParameter(stateInfo));
}
}

这里我们看到了,这个里面使用了一个自定义的状态参数CollectStateInfo ,这个是我们用来在后台进程处理过程中传递的一个对象,可以记录当前采集的相关信息,CollectStateInfo 类的定义如下所示。

    /// <summary>
/// 状态对象数据
/// </summary>
public class CollectStateInfo
{
/// <summary>
/// 当前期数(年份+期数)
/// </summary>
public string YearQSNumber { get; set; } /// <summary>
/// 任务开始时间
/// </summary>
public DateTime StartTime { get; set; } private DateTime m_EndTime = DateTime.Now; /// <summary>
/// 任务开始时间
/// </summary>
public DateTime EndTime
{
get
{
return m_EndTime;
}
set
{
//设置结束时间的时候,获取耗时
m_EndTime = value;
this.TimeSpanUsed = value.Subtract(this.StartTime);
}
} /// <summary>
/// 任务用时
/// </summary>
public TimeSpan TimeSpanUsed { get; set; } /// <summary>
/// 任务数量
/// </summary>
public int TotalRecords { get; set; } private int m_CompletedRecord = ; /// <summary>
/// 完成数量
/// </summary>
public int CompletedRecord
{
get
{
return m_CompletedRecord;
}
set
{
m_CompletedRecord = value;
if (TotalRecords > )
{
this.CurrentProgress = Convert.ToInt32(value * 100.0 / TotalRecords);
}
}
} /// <summary>
/// 当前进度
/// </summary>
public int CurrentProgress { get; set; } /// <summary>
/// 当前采集的项目
/// </summary>
public string CurrentItemName { get; set; } /// <summary>
/// 默认构造函数
/// </summary>
/// <param name="total"></param>
public CollectStateInfo()
{
this.StartTime = DateTime.Now;
this.EndTime = DateTime.Now;
} /// <summary>
/// 构造函数
/// </summary>
/// <param name="total">任务数量</param>
/// <param name="qsNumber">采集当前期数</param>
public CollectStateInfo(int total, string qsNumber, int completed) :this()
{
this.TotalRecords = total;
this.YearQSNumber = qsNumber;
this.CompletedRecord = completed;
} }

上面的对象,主要用来记录任务的总数,以及当前进行的数量,还包括一些其他信息,如任务的开始时间,结束时间等等,我们可以把一些常规的任务信息,放到这里面来传递即可。

另一个后台进程处理的关键事件就是处理过程的代码实现,主要就是采集处理的逻辑内容,如下所示。

        void worker_DoWork(object sender, DoWorkEventArgs e)
{
CollectStateInfo info = e.Argument as CollectStateInfo;
if (info != null)
{
LinkJob job = new LinkJob();
var stateInfo = job.Execute(this.worker, info);
e.Result = stateInfo;
}
}

这个里面我么主要到它的e.Argument 就是我们传递的对象,通过类型转换我们就可以获得对应的信息,然后进行具体的处理了。

另外一个就是当整个后台进程完成处理后,我们需要进行相关的提示和状态处理,实现代码如下所示。

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//还原按钮状态
InitCollectState();
IsThreadRunning = false; string message = "采集操作完成";
CollectStateInfo stateInfo = e.Result as CollectStateInfo;
if (stateInfo != null && stateInfo.CompletedRecord == stateInfo.TotalRecords)
{
message += string.Format(",完成采集网址{0}个,耗时为:{1}分钟{2}秒。", stateInfo.TotalRecords, stateInfo.TimeSpanUsed.Minutes, stateInfo.TimeSpanUsed.Seconds); //清空数据即可
JobParameterHelper.ClearData();
}
else
{
message += string.Format(",用户取消处理,耗时为:{1}分钟{2}秒。", stateInfo.TotalRecords, stateInfo.TimeSpanUsed.Minutes, stateInfo.TimeSpanUsed.Seconds);
}
MessageDxUtil.ShowTips(message);
}

而我们开始任务,则通过按钮触发后台线程的异步接口调用即可,如下代码所示。

                if (!worker.IsBusy)
{
this.btnStartCollect.ImageOptions.Image = Resources.Button_Stop;
this.lblTips.Text = "数据采集中....,单击按钮可停止采集";
this.btnStartCollect.Text = "停止采集"; var totalCount = BLLFactory<URLLink>.Instance.GetRecordCount();//数量为总数
var stateInfo = new CollectStateInfo(totalCount, yearQSNumber, skipCount); worker.RunWorkerAsync(stateInfo);
//改变状态
IsThreadRunning = !IsThreadRunning;
}

这里面我们设置提示开始采集数据后,然后构建一个可以用于传递的线程采集对象给后台线程,通过异步调用worker.RunWorkerAsync(stateInfo); 即可实现任务的开始操作。

如果任务总之,我们调用取消接口即可。

                if (MessageDxUtil.ShowYesNoAndWarning("采集正在进行中,您确认停止采集吗?") == System.Windows.Forms.DialogResult.Yes)
{
worker.CancelAsync(); //改变状态
IsThreadRunning = !IsThreadRunning; //还原按钮状态
InitCollectState();
}

启动采集界面进行相应的处理即可,如下所示。

采集过程的进度可以通过状态栏实时的显示出来,这个有赖于我们定义的状态类,可以很方便进行UI的信息通知。

以上就是使用后台 线程BackgroundWorker处理任务的一些总结,希望给读者带来一些参考价值,在我们做一些耗时的操作的时候,可以考虑使用这个后台线程BackgroundWorker处理任务,从而实现较好的界面通知,也不会造成UI界面的停顿卡死状态。

使用后台线程BackgroundWorker处理任务的总结的更多相关文章

  1. 开源WinForms界面开发框架Management Studio 选项卡文档 插件 Office 2007蓝色风格 后台线程

    Management Studio是我在WinForms小项目开发过程中搭建起来的一个插件式结构的应用程序框架,因为简单灵活又容易扩展,现在将它开源供读者参考. 跑起来的效果图如下所示,具备选项卡式多 ...

  2. 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

  3. C#夯实基础之多线程二:主线程、前台线程与后台线程

    我们在<C#夯实基础之多线程一:初识多线程>一文中第二部分中指出,既然windows最终发展出了多线程模型,按理说,我们直接使用一个.NetFramework的线程类就可以直接撸代码了,但 ...

  4. HandlerThread 创建一个异步的后台线程

    使用HandlerThread几大优点: 1.制作一个后台异步线程,需要的时候就可以丢一个任务给它,使用比较灵活; 2.Android系统提供的,使用简单方便,内部自己封装了Looper+Handle ...

  5. Android的UI设计与后台线程交互

    本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的流畅运行.本文还将阐述一些用户界面(UI)中与线程交互的API.UI用户界面 ...

  6. 掌握GCD以及后台永久运行的代码 (使用GCD处理后台线程和UI线程的交互)

    一个例子: 在iPhone上做一个下载网页的功能,就是:在iPhone上放一个按钮,单击按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成后,将内容加载到界面上的一个文本控件上. 使用GCD前: ...

  7. Java并发编程(三)后台线程(Daemon Thread)

    后台线程,守护线程(Daemon Thread) 所谓的后台线程,就是指这种线程并不属于程序中不可或缺的部分,因此当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程.通过setD ...

  8. Android Priority Job Queue (Job Manager):后台线程任务结果传回前台(三)

     Android Priority Job Queue (Job Manager):后台线程任务结果传回前台(三) 在附录文章4,5的基础上改造MainActivity.java和MyJob.ja ...

  9. Java多线程之后台线程不执行finally

    后台线程不执行finally package wzh.daemon; import java.util.concurrent.TimeUnit; class ADaemon implements Ru ...

随机推荐

  1. Linux 中提高的 SSH 的安全性

    SSH 是远程登录 Linux 服务器的最常见的方式.且 SSH 登录的时候要验证的,相对来讲会比较安全.那只是相对,下面会介绍一些方式提高 SSH 的安全性 SSH 的验证 而SSH 登录时有两种验 ...

  2. Centos 中无法上网的问题

    我是 Centos 最小化安装的,安装网后 Centos 竟然无法上网...有点奇葩, 应该是网卡没有激活的问题了,下面是解决的过程 查看网卡 ip addr 其中 lo 是 Loop back ad ...

  3. python如何实现类似php的引用赋值

    直接放代码,有注释,就不解析了 # ############################ # 改变一个变量的值,与之有关系的变量的值也会相等变化 # 类似php的引用赋值,宏观来看 # ##### ...

  4. C#-多态(十二)

    继承概念 多态:即一个接口,多个功能 同一种操作作用于不同的对象,可以有不同的解释,产生不同的执行结果 多态性可以是静态的或动态的.在静态多态性中,函数的响应是在编译时发生的.在动态多态性中,函数的响 ...

  5. C#-基本语法(三)

    关键词 关键字,是一些被C#规定了用途的重要单词 在Visual Studio的开发环境中,关键字被标识为蓝色 例如: using:导入命名空间 class:声明类 static:静态 void:无返 ...

  6. 服务器体系(SMP, NUMA, MPP)与共享存储器架构(UMA和NUMA)

    1. 3种系统架构与2种存储器共享方式 1.1 架构概述 从系统架构来看,目前的商用服务器大体可以分为三类 对称多处理器结构(SMP:Symmetric Multi-Processor) 非一致存储访 ...

  7. Unity与安卓开发的一些路径知识

    APK安装之后找不到路径 公司的测试机(安卓)基本都是不带SD卡的. APK在安卓手机上安装之后,使用手机助手类的软件打开文件管理,打开 内置SDK卡/Android/data/ 在这个目录下却发现 ...

  8. Win10改AHCI无需重装系统(无需改注册表)的方法

    下面就开始:1.开机后按下WIN键 加 R键2.输入 msconfig3.如图中所示进行点击.1 引导界面 2安全引导打钩 .最小打钩 3 下面的确定.4.点击重新启动5.在重启时连续按 F2 进入B ...

  9. Tomcat配置自定义JAVA环境

    Tomcat的bin目录下 在setclasspath.sh(Linux系统,Windows系统为setclasspath.bat)文件的开头添加: export JAVA_HOME=/usr/lib ...

  10. LeetCode算法题-String Compression(Java实现)

    这是悦乐书的第230次更新,第242篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第97题(顺位题号是443).给定一组字符,就地压缩它.压缩后的长度必须始终小于或等于原 ...