使用后台线程BackgroundWorker处理任务的总结
在一些耗时的操作过程中,在长时间运行时可能会导致用户界面 (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处理任务的总结的更多相关文章
- 开源WinForms界面开发框架Management Studio 选项卡文档 插件 Office 2007蓝色风格 后台线程
Management Studio是我在WinForms小项目开发过程中搭建起来的一个插件式结构的应用程序框架,因为简单灵活又容易扩展,现在将它开源供读者参考. 跑起来的效果图如下所示,具备选项卡式多 ...
- 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程
额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...
- C#夯实基础之多线程二:主线程、前台线程与后台线程
我们在<C#夯实基础之多线程一:初识多线程>一文中第二部分中指出,既然windows最终发展出了多线程模型,按理说,我们直接使用一个.NetFramework的线程类就可以直接撸代码了,但 ...
- HandlerThread 创建一个异步的后台线程
使用HandlerThread几大优点: 1.制作一个后台异步线程,需要的时候就可以丢一个任务给它,使用比较灵活; 2.Android系统提供的,使用简单方便,内部自己封装了Looper+Handle ...
- Android的UI设计与后台线程交互
本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的流畅运行.本文还将阐述一些用户界面(UI)中与线程交互的API.UI用户界面 ...
- 掌握GCD以及后台永久运行的代码 (使用GCD处理后台线程和UI线程的交互)
一个例子: 在iPhone上做一个下载网页的功能,就是:在iPhone上放一个按钮,单击按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成后,将内容加载到界面上的一个文本控件上. 使用GCD前: ...
- Java并发编程(三)后台线程(Daemon Thread)
后台线程,守护线程(Daemon Thread) 所谓的后台线程,就是指这种线程并不属于程序中不可或缺的部分,因此当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程.通过setD ...
- Android Priority Job Queue (Job Manager):后台线程任务结果传回前台(三)
Android Priority Job Queue (Job Manager):后台线程任务结果传回前台(三) 在附录文章4,5的基础上改造MainActivity.java和MyJob.ja ...
- Java多线程之后台线程不执行finally
后台线程不执行finally package wzh.daemon; import java.util.concurrent.TimeUnit; class ADaemon implements Ru ...
随机推荐
- C#:读取视频的宽度和高度等信息
读取方式:使用ffmpeg读取,所以需要先下载ffmpeg.网上资源有很多. 通过ffmpeg执行一条CMD命令可以读取出视频的帧高度和帧宽度信息. 如图: 蓝线框中可以看到获取到的帧高度和帧宽度. ...
- Kotlin入门(24)如何自定义视图
Android提供了丰富多彩的视图与控件,已经能够满足大部分的业务需求,然而计划赶不上变化,总是有意料之外的情况需要特殊处理.比如PagerTabStrip无法在布局文件中指定文本大小和文本颜色,只能 ...
- aspectj 简单的模拟权限检查、事务、日志记录
package com.ij34.service; public class Hello { public void he() { System.out.println("执行Hello的h ...
- [20180918]文件格式与sql_id.txt
[20180918]文件格式与sql_id.txt --//记录测试中遇到的一个问题.这是我在探究SQL*Net more data from client遇到的问题.--//就是实际oracle会把 ...
- 通过Socket实现TCP编程,用户登录之服务器相应客户端,客户端和服务端之间的通信
服务器端: 1.创建ServerSocket对象,绑定监听端口: 2.通过accept()方法监听客户端请求: 3.建立连接后通过输入流读取客户端发送的请求信息; 4.通过输出流向客户端发送响应信息; ...
- 4.7Python数据处理篇之Matplotlib系列(七)---matplotlib原理分析
目录 目录 前言 (一)总框架分析 (二)函数式的绘图 1.说明: 2.函数绘图的缺优点 3.绘图类的函数 4.操作类的函数 5.例子: (三)面向对象式的绘图 1.基本概念 2.基本对象 3.面向对 ...
- css点滴2—六种方式实现元素水平居中
本文参考文章<六种方式实现元素水平居中> 元素水平居中的方法,最常见的莫过于给元素一个显式的宽度,然后加上margin的左右值为auto.这种方式给固定宽度的元素设置居中是最方便不过的.但 ...
- 我的第一个SolidWorks图
1. 学习到的知识点 2. 完成的工程图 3. 感受 学习是一种快乐,学到新的知识要学会分享,只要坚持,就有那么一点点的成就. 4. 参考 SolidWorks帮助文档
- 06.Python网络爬虫之requests模块(2)
今日内容 session处理cookie proxies参数设置请求代理ip 基于线程池的数据爬取 知识点回顾 xpath的解析流程 bs4的解析流程 常用xpath表达式 常用bs4解析方法 引入 ...
- 【Teradata】磁盘碎片整理(ferret工具)
DEFRAGMENTcombines free sectors and moves them to the end of a cylinder.PACKDISKfill (or packs) cyli ...