好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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 = 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界面的停顿卡死状态。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/wuhuacong/p/9144424.html

dy("nrwz");

查看更多关于C#使用后台线程BackgroundWorker处理任务的总结的详细内容...

  阅读:50次