zoukankan      html  css  js  c++  java
  • 使用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(); 
    应该首先想到依赖线程池:
    1. ThreadPool.QueueUserWorkItem((objState) => 
    2. {  
    3.     //工作代码  
    4. }, null); 

    线程池技术能让我们重点关注业务的实现,而不是线程的性能测试。

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

    1. private BackgroundWorker worker;  
    2.  
    3. private void startAsyncButton_Click(System.Object sender,  
    4.     System.EventArgs e)  
    5. {  
    6.     worker.DoWork += new DoWorkEventHandler(worker_DoWork);  
    7.     worker.ProgressChanged += new   
    8.         ProgressChangedEventHandler(worker_ProgressChanged);  
    9.     worker.RunWorkerAsync();  
    10. }  
    11.  
    12. private void worker_DoWork(object sender, DoWorkEventArgs e)  
    13. {  
    14.     BackgroundWorker worker = sender as BackgroundWorker;  
    15.     for (int i = 0; i < 10; i++)  
    16.     {  
    17.         worker.ReportProgress(i);  
    18.         Thread.Sleep(100);  
    19.     }  
    20. }  
    21.  
    22. private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)  
    23. {  
    24.     this.label1.Text = e.ProgressPercentage.ToString();  
    该示例是一个Winform窗体程序,正在从事Winform或WPF开发的人员,可考虑使用BackgroundWorker。
  • 相关阅读:
    spring cloud config 属性加解密
    IntelliJ IDEA 快捷键
    SQL Server 2012 安装图解教程(附sql2012下载地址)
    spring cloud 启动报错-must be declared as an @AliasFor [serviceId], not [name].
    MySQL主从复制配置
    恢复MySQL数据库删除的数据
    java.lang.IllegalStateException: No instances available for localhost
    蜘蛛牌 (DFS)
    加油站问题 (优先队列)
    堆的操作的复杂度
  • 原文地址:https://www.cnblogs.com/shihao/p/2456818.html
Copyright © 2011-2022 走看看