改善C#程序的157个建议(连载79)

发表于:2011-11-25 11:04

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:陆敏技    来源:51Testing软件测试网采编

#
DotNet
分享:

  建议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根据自身的算法来做这个决定。所以,如果我们要多线程编码,不应想到:

  • Thread t = new Thread(() => 
  •     {  
  •         //工作代码  
  •     });  
  • t.Start();
  •   应该首先想到依赖线程池:

  • ThreadPool.QueueUserWorkItem((objState) => 
  • {  
  •     //工作代码  
  • }, null);
  • 21/212>
    《2023软件测试行业现状调查报告》独家发布~

    关注51Testing

    联系我们

    快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

    法律顾问:上海兰迪律师事务所 项棋律师
    版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
    投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

    沪ICP备05003035号

    沪公网安备 31010102002173号