C#使用后台线程BackgroundWorker处理任务的总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C#使⽤后台线程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 = erState as CollectStateInfo;
if (stateInfo != null)
{
var message = string.Format("正在采集 {0} 的 {1} , 项⽬名称为:{2}", stateInfo.TotalRecords, pletedRecord + 1, 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 = 0;
/// <summary>
/// 完成数量
/// </summary>
public int CompletedRecord
{
get
{
return m_CompletedRecord;
}
set
{
m_CompletedRecord = value;
if (TotalRecords > 0)
{
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;
pletedRecord = 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 && pletedRecord == 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界⾯的停顿卡死状态。
希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。