在使用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 + 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;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界面的停顿卡死状态。
DevExpress | 下载试用
DevExpress Universal 10月正式发布今年第二个重大版本——v21.2,此版本正式官宣支持Visual Studio 2022 & .NET6,同时与微软最新发布的Windows 11完美兼容,全面解决用户各种使用场景问题。 与时俱进,从未止步!
DevExpress技术交流群5:742234706 欢迎一起进群讨论

标签:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!