服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#使用后台线程BackgroundWorker处理任务的总结

C#使用后台线程BackgroundWorker处理任务的总结

2022-02-25 14:39伍华聪 C#

这篇文章主要介绍了C#使用后台线程BackgroundWorker处理任务的总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

采集的数据处理

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

C#使用后台线程BackgroundWorker处理任务的总结

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

?
1
private backgroundworker worker = new backgroundworker();

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;    //报告进度
    }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <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 类的定义如下所示。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/// <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;
    }
 
  }

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

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

?
1
2
3
4
5
6
7
8
9
10
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 就是我们传递的对象,通过类型转换我们就可以获得对应的信息,然后进行具体的处理了。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
    }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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); 即可实现任务的开始操作。

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

?
1
2
3
4
5
6
7
8
9
10
if (messagedxutil.showyesnoandwarning("采集正在进行中,您确认停止采集吗?") == system.windows.forms.dialogresult.yes)
        {
          worker.cancelasync();
 
          //改变状态
          isthreadrunning = !isthreadrunning;
 
          //还原按钮状态
          initcollectstate();
        }

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

C#使用后台线程BackgroundWorker处理任务的总结

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

C#使用后台线程BackgroundWorker处理任务的总结

以上就是使用后台线程backgroundworker处理任务的一些总结,希望给读者带来一些参考价值,在我们做一些耗时的操作的时候,可以考虑使用这个后台线程backgroundworker处理任务,从而实现较好的界面通知,也不会造成ui界面的停顿卡死状态。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

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

延伸 · 阅读

精彩推荐
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16