一、线程池

每次创建一个线程,都会花费几百微秒级别的时间来创建一个私有的局部栈,每个线程默认使用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. iOS三种定时器的用法NSTimer、CADisplayLink、GCD

    一,NSTimer //创建方式1 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector: ...

  2. ConvertHelper与泛型集合

    在机房重构时.我们常常会用到ConvertHelper. 它把从数据库中查询到的dateTable(也是一个暂时表)转化为泛型,然后再填充到DataGridView控件中. ConvertHelper ...

  3. 详解 MySQL 中的 explain

    来源:persister 链接:http://www.blogjava.net/persister/archive/2008/10/27/236813.html 在 explain的帮助下,您就知道什 ...

  4. com.classpath.www

    package com.classpath.www; import java.io.File; import java.net.URL; import javax.servlet.ServletCon ...

  5. 利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档

    对于DBA或开发来说,如何规范化你的数据库表结构文档是灰常之重要的一件事情.但是当你的库,你的表排山倒海滴多的时候,你就会很头疼了. 推荐一款工具DBExportDoc V1.0 For MySQL( ...

  6. Win7无线网络共享设置方法

    http://jingyan.baidu.com/article/4f34706e89bb2ae387b56d0b.html

  7. [转]在BBB启动时自动加载dtbo(或执行脚本、运行程序)

    启动时自动加载dtbo,实际上就是做了一个cape.官方推荐的方法是用eeprom来实现,请参考我的博文<为BBB制作专属自己的cape(一)>和<为BBB制作专属自己的cape(四 ...

  8. the first assignment of software testing

    Github ID:  bzdwdmzjsmff Github address: https://github.com/bzdwdmzjsmff alternative article: Increa ...

  9. 为Debain &&Centos安装dig

    Debain&Ubuntu sudo apt-get install dnsutils Fdeoar&Centos yum install bind-utils

  10. [转载][记录]javascript生成不重复的随机数

    参考链接:javascript生成不重复的随机数 项目播放视频,是无序的,有上下两个按钮,所以需要生成1,8不重复的随机数数组,如: ,,,,,,, 然后再split一次,就是数组了. 拿来主义了