加入收藏 | 设为首页 | 会员中心 | 我要投稿 北几岛 (https://www.beijidao.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

使用ThreadPool或BackgroundWorker代替Thread

发布时间:2021-05-20 14:32:54 所属栏目:大数据 来源: https://blog.csdn.net/kasama1
导读:建议79:使用ThreadPool或BackgroundWorker代替Thread 2011-09-19 22:08 陆敏技 机械工业出版社? 我要评论( 0 ) ?字号: T ?|? T 综合评级: 想读(45)??在读(16)??已读(2)???品书斋鉴(0)???已有63人发表书评 《编写高质量代码:改善C#程序的157个建议》第6章

建议79:使用ThreadPool或BackgroundWorker代替Thread

2011-09-19 22:08 陆敏技 机械工业出版社? 我要评论(0)?字号: T?|? T

综合评级:

想读(45)??在读(16)??已读(2)???品书斋鉴(0)???已有63人发表书评

一键收藏,随时查看,分享好友!

《编写高质量代码:改善C#程序的157个建议》第6章异步、多线程、任务和并行,本章将通过具体的实例,让我们熟悉异步、多线程、任务和并行。我们会了解到异步的实质、任务的实质,以及为什么有了任务还需要一个并行类(Parallel)等问题。同时,本章内容还会告诉我们如何优雅地控制线程,并且处理任务和并行中的异常。本节为大家介绍建议79:使用ThreadPool或BackgroundWorker代替Thread。

AD:51CTO 网+ 第十二期沙龙:大话数据之美_如何用数据驱动用户体验

建议79:使用ThreadPool或BackgroundWorker代替Thread

使用线程能极大地提升用户体验度,但是作为开发者应该注意到,线程的开销是很大的。

线程的空间开销来自:

1)线程内核对象(Thread Kernel Object)。每个线程都会创建一个这样的对象,它主要包含线程上下文信息,在32位系统中,它所占用的内存在700字节左右。

2)线程环境块(Thread Environment Block)。TEB包括线程的异常处理链,32位系统中占用4KB内存。

3)用户模式栈(User Mode Stack),即线程栈。线程栈用于保存方法的参数、局部变量和返回值。每个线程栈占用1024KB的内存。要用完这些内存很简单,写一个不能结束的递归方法,让方法参数和返回值不停地消耗内存,很快就会发生OutOfMemoryException。

4)内核模式栈(Kernel Mode Stack)。当调用操作系统的内核模式函数时,系统会将函数参数从用户模式栈复制到内核模式栈。在32位系统中,内核模式栈会占用12KB内存。

线程的时间开销来自:

1)线程创建的时候,系统相继初始化以上这些内存空间。

2)接着CLR会调用所有加载DLL的DLLMain方法,并传递连接标志(线程终止的时候,也会调用DLL的DLLMain方法,并传递分离标志)。

3)线程上下文切换。一个系统中会加载很多的进程,而一个进程又包含若干个线程。但是一个cpu在任何时候都只能有一个线程在执行。为了让每个线程看上去都在运行,系统会不断地切换“线程上下文”:每个线程大概得到几十毫秒的执行时间片,然后就会切换到下一个线程了。这个过程大概又分为以下5个步骤:

步骤1 进入内核模式。

步骤2 将上下文信息(主要是一些cpu 寄存器信息)保存到正在执行的线程内核对象上。

步骤3 系统获取一个 Spinlock,并确定下一个要执行的线程,然后释放 Spinlock。如果下一个线程不在同一个进程内,则需要进行虚拟地址交换。

步骤4 从将被执行的线程内核对象上载入上下文信息。

步骤5 离开内核模式。

由于要进行如此多的工作,所以创建和销毁一个线程就意味着代价“昂贵”。为了避免程序员无节制地使用线程,微软开发了“线程池”技术。简单来说,线程池就是替开发人员管理工作线程。当一项工作完毕时,CLR不会销毁这个线程,而是会保留这个线程一段时间,看是否有别的工作需要这个线程。至于何时销毁或新起线程,由CLR根据自身的算法来做这个决定。所以,如果我们要多线程编码,不应想到:

   
  1. Thread?t?=?new?Thread(()?=>?
  2. ????{ ?
  3. ????????//工作代码 ?
  4. ????}); ?
  5. t.Start();?

应该首先想到依赖线程池:
  • ThreadPool.QueueUserWorkItem((objState)?=>?
  • { ?
  • ????//工作代码 ?
  • },?null);?
  • 线程池技术能让我们重点关注业务的实现,而不是线程的性能测试。

    本建议还提到了一个类型BackgroundWorker。BackgroundWorker是在内部使用了线程池的技术;同时,在Winform或WPF编码中,它还给工作线程和UI线程提供了交互的能力。如果我们稍加注意,就会发现:Thread和ThreadPool默认都没有提供这种交互能力,而BackgroundWorker却通过事件提供了这种能力。这种能力包括:报告进度、支持完成回调、取消任务、暂停任务等。一个使用BackgroundWorker的简单示例如下:

  • private?BackgroundWorker?worker; ?
  • ?
  • private?void?startAsyncButton_Click(System.Object?sender,?
  • ????System.EventArgs?e) ?
  • { ?
  • ????worker.DoWork?+=?new?DoWorkEventHandler(worker_DoWork); ?
  • ????worker.ProgressChanged?+=?new? ?
  • ????????ProgressChangedEventHandler(worker_ProgressChanged); ?
  • ????worker.RunWorkerAsync(); ?
  • } ?
  • ?
  • private?void?worker_DoWork(object?sender,?DoWorkEventArgs?e) ?
  • { ?
  • ????BackgroundWorker?worker?=?sender?as?BackgroundWorker; ?
  • ????for?(int?i?=?0;?i?<?10;?i++) ?
  • ????{ ?
  • ????????worker.ReportProgress(i); ?
  • ????????Thread.Sleep(100); ?
  • ????} ?
  • } ?
  • ?
  • private?void?worker_ProgressChanged(object?sender,?ProgressChangedEventArgs?e) ?
  • { ?
  • ????this.label1.Text?=?e.ProgressPercentage.ToString(); ?
  • }?
  • 该示例是一个Winform窗体程序,正在从事Winform或WPF开发的人员,可考虑使用BackgroundWorker。

    (编辑:北几岛)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读