一、线程池

每次创建一个线程,都会花费几百微秒级别的时间来创建一个私有的局部栈,每个线程默认使用1M的内存。这个可以在使用Thread类的构造函数时设置:

  1. new Thread(new ThreadStart(Go), 2);
  2. new Thread(new ParameterizedThreadStart(Go("hello")), 3);

提供的两种构造函数方式都提供了对应的设置线程局部栈的大小。线程池通过共享和回收线程的方式来分配这些内存,这样可以使多线程运行在一个非常细粒度级别上而不影响性能。这对于充分利用多核处理器,使用分而治之的方式进行密集型计算的程序中很有用。同时线程池维护一个所有同时运行的工作线程总数的上限,如果有过多的活动线程就会加重操作系统的负担,使诸如CPU缓存失效等问题,当达到这个上限后,就要进行排队。这个线程队列使得任意并发的应用成为可能,如Web服务器就是这种原理。

有多种方式进入线程池:

  • 通过Task Parallel Library(.NET 4  TPL)
  • 通过调用ThreadPool.QueueUserWorkItem
  • 通过异步委托方式
  • 使用BackgroundWorker
下面的应用间接地使用了线程池:
  • WCF、Remoting、ASP.NET、ASMX webservice
  • System.Timers.Timer和System.Threading.Timer
  • .NET中以Async结束命名的方法
  • PLINQ
注意事项:
  • 不能对放入线程池中的线程命名,这会使得调试更加困难
  • 线程池中的线程总是后台线程
  • 阻塞线程池中的线程困难引发额外的延迟
可以使用下面的方式查询当前线程是否在线程池中:
  1. CurrentTread.IsThreadPoolThread

二、进入线程池

1、使用TPL进入
使用TPL中的Task类就可以很简单的进入,使用Task.Factory.StartNew方法,传递一个目标函数的委托即可:
  1. static void Main(string[] args)
  2. {
  3. Task.Factory.StartNew(Go);
  4. Console.WriteLine("Is Thread Pool: " + Thread.CurrentThread.IsThreadPoolThread.ToString());
  5. Console.ReadKey();
  6. }
  7. static void Go()
  8. {
  9. Console.Write("Is Thread Pool: " + Thread.CurrentThread.IsThreadPoolThread.ToString());
  10. }


Task.Factory.StartNew返回一个Task对象可以用来监控这个任务。

同时可以使用泛型类Task<TResult>,如下:
  1. static void Main(string[] args)
  2. {
  3. Task<string> task = Task.Factory.StartNew<string>(Go);
  4. Console.WriteLine("Is Thread Pool: " + Thread.CurrentThread.IsThreadPoolThread.ToString());
  5. if (task.IsCompleted)
  6. {
  7. string result = task.Result;
  8. Console.WriteLine(task.IsCompleted.ToString() +  result);
  9. }
  10. Console.ReadKey();
  11. }
  12. static string Go()
  13. {
  14. return Thread.CurrentThread.IsThreadPoolThread.ToString();
  15. }


如果在工作线程中出现异常,当获取Task的Result属性时会重新引发AggregateException异常,如果没有查询Result或者没有调用Wait方法,所有未处理的异常都会终止进程执行。

2、不使用TPL
对于.NET 4.0以前的版本是无法使用TPL的,必须使用ThreadPool.QueueUserWorkItem(类似Task类功能)和异步委托(类似Task<TResult>),但是这两个没有前面所述方法快、也没有那么方便和很好的扩展性。两种方式的使用如下:
  1. static void Main()
  2. {
  3. ThreadPool.QueueUserWorkItem (Go);
  4. ThreadPool.QueueUserWorkItem (Go, 123);
  5. Console.ReadLine();
  6. }
  7. static void Go (object data)   // data will be null with the first call.
  8. {
  9. Console.WriteLine ("Hello from the thread pool! " + data);
  10. }
  1. static void Main()
  2. {
  3. Func<string, int> method = Work;
  4. method.BeginInvoke ("test", Done, method);
  5. // ...
  6. //
  7. }
  8. static int Work (string s) { return s.Length; }
  9. static void Done (IAsyncResult cookie)
  10. {
  11. var target = (Func<string, int>) cookie.AsyncState;
  12. int result = target.EndInvoke (cookie);
  13. Console.WriteLine ("String length is: " + result);
  14. }

三、线程池的优化

线程池拥有的最大线程数可以通过ThreadPool.SetMaxThreads设置,默认值如下:
  • 32位的.NET4.0环境为1023个
  • 64位的.NET4.0环境为32768个
  • .NET 3.5为每个CPU核 250个
  • .NET2.0 为每个CPU核25个
线程池管理器在分配任务时通过添加新的线程来应对额外的工作量,当达到极限后开始排队。在非活动的闲置期,管理器可以删除一些可疑的线程从而可以得到更好的吞吐量。当然也可以使用Thread.SetMinThreads来设置最小的线程数目:最小线程数是优化线程池的高级方式,这个将指导管理器对于线程的分配没有延迟。提高最小数目可以当在有阻塞线程时提高并发量。
提高线程的最小数目并不能保证至少有相应个线程在线程池中,线程只会在有需要的时候创建。同时这个会指导池管理器创建到最小数目的线程。线程池在分配线程时会有半秒的延迟,之所以要有这个延迟就是为了防止突然的大量线程分配导致应用程序内存占用过大。线程池对于队列保持等待超过半秒时就会通过创建新的线程来响应请求,没半秒创建一个只到达到最大线程数。
这个半秒的延迟是有利也有弊的,弊端在于当一个阻塞的线程出现时并不需要延迟半秒,例如一个查询数据库或者下载网页的线程。这时需要告诉池管理器不要延迟进程线程分配,因此就需要用到SetMinThreads来设置:
  1. ThreadPool.SetMinThreads(50,50);
 

C#多线程(二)的更多相关文章

  1. java 多线程二

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 线程中断: /** * Created by root on 17-9-30. */ public class Test ...

  2. java基础-多线程二

    java基础-多线程二 继承thread和实现Runnable的多线程每次都需要经历创建和销毁的过程,频繁的创建和销毁大大影响效率,线程池的诞生就可以很好的解决这一个问题,线程池可以充分的利用线程进行 ...

  3. C#夯实基础之多线程二:主线程、前台线程与后台线程

    我们在<C#夯实基础之多线程一:初识多线程>一文中第二部分中指出,既然windows最终发展出了多线程模型,按理说,我们直接使用一个.NetFramework的线程类就可以直接撸代码了,但 ...

  4. Java:多线程<二> 同步

    由于多线程的访问出现延迟和线程的随机性,在使用多线程时往往会伴随安全性的问题,这些问题一旦出现将会是非常严重的.为了解决这种安全性问题,synchronized出现了. synchronized用法一 ...

  5. Java多线程——<二>将任务交给线程,线程声明及启动

    一.任务和线程 <thinking in java>中专门有一小节中对线程和任务两个概念进行了具体的区分,这也恰好说明任务和线程是有区别的. 正如前文所提到的,任务只是一段代码,一段要达成 ...

  6. 从零开始学习Java多线程(二)

    前面已经简单介绍进程和线程,为后续学习做铺垫.本文讨论多线程传参,Java多线程异常处理机制. 1. 多线程的参数传递 在传统开发过程中,我们习惯在调用函数时,将所需的参数传入其中,通过函数内部逻辑处 ...

  7. 多线程二:线程池(ThreadPool)

    在上一篇中我们讲解了多线程的一些基本概念,并举了一些例子,在本章中我们将会讲解线程池:ThreadPool. 在开始讲解ThreadPool之前,我们先用下面的例子来回顾一下以前讲过的Thread. ...

  8. Java多线程(二) —— 深入剖析ThreadLocal

    对Java多线程中的ThreadLocal类还不是很了解,所以在此总结一下. 主要参考了http://www.cnblogs.com/dolphin0520/p/3920407.html 中的文章. ...

  9. python多线程(二)

    原文:http://blog.sina.com.cn/s/blog_4b5039210100esc1.html 基础不必多讲,还是直接进入python. Python代码代码的执行由python虚拟机 ...

  10. 并发和多线程(二)--线程安全、synchronized、CAS简介

    线程安全性: 当多个线程访问一个类的时候,这个类始终表示出正确的行为,那么这个类是线程安全的. 无状态的对象一定是线程安全的,例如大部分service.dao.Servlet都是无状态的. 线程安全体 ...

随机推荐

  1. MySQL监控模板说明-Percona MySQL Monitoring Template for Cacti

    http://blog.chinaunix.net/uid-16844903-id-3535535.html https://www.percona.com/doc/percona-monitorin ...

  2. taobao月报 ---mysql汇总

    http://blog.csdn.net/qiuyepiaoling/article/category/709481

  3. Retina屏下1px border

    layout tltle tags post ios7下移动web开发的几个坑 webapp 1.Retina屏下1px border 由于高清屏的特性,1px是由2×2个像素点来渲染,那么我们样式上 ...

  4. 深入理解C++中的mutable关键字

      mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词. 在C++中,mutable也是为了突破const的限制而设置的.被mutable修饰的变量,将 ...

  5. mysql使用心得

    SET FOREIGN_KEY_CHECKS=0; -- ------------------------------ Table structure for `staff`-- ---------- ...

  6. Basic Example of JMX Technology--转载

    原文地址:http://nick-lab.gs.washington.edu/java/jdk1.5b/guide/jmx/tutorial/connectors.html Basic Example ...

  7. Asp.Net 5使用第三方容器

    这几天在学习Asp.Net 5,现在文档以及博客之类的资料实在太少了,不看源码几乎举步维艰,好在全都是开源的,看看微软的代码也获益良多. 看到DependencyInjection的代码里除了默认的容 ...

  8. Cstring到string

    要利用mfc,然后接受一个图片. imread只能读const string& filename 的东西. imread 原型: CV_EXPORTS_W Mat imread( ); 它的参 ...

  9. javah 生成header file 报错 问题解决

    环境:Android Studio, Mac OS 目标: 用javah 为MainActivity.class 生成 jni header 文件 正确的命令是 cd <class文件的路径&g ...

  10. js原生bind()用法[注意不是jquery里面的bind()]

    <div id="a"> <div></div> <div></div> <div></div> ...