C#多线程之旅(3)——线程池
先交代下背景,写《C#多线程之旅》这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很多问题。

多线程,有利也有弊,使用需谨慎。
原文地址:C#多线程之旅(3)——线程池
C#多线程之旅(5)——同步机制介绍
C#多线程之旅(6)——详解多线程中的锁
更多文章正在更新中,敬请期待......
C#多线程之旅(3)——线程池
代码下载
Thread_博客园_cnblogs_jackson0714.zip
第一篇~第三篇的代码示例:

源码地址:https://github.com/Jackson0714/Threads
一、介绍
无论你什么时候开始一个线程,几百毫秒会花在整理一个新的local variable stack。每一个线程默认会消耗1MB的内存。线程池通过分享和回收线程来削减这些开销,允许多线程被应用在一个非常颗粒级的级别而没有性能损失。当充分利用多核系统去执行密集型计算的并行代码时这是非常有用的。
线程池也会在线程的总数量上保持一个限制,从而使线程能够更平稳地运行。太多的线程将会造成管理负担和使CPU缓存是小,从而造成操作系统不能运行。一旦一个限制到达,job排队等待直到另外一个完成才开始。这会使任意的并行应用程序成为可能,比如一个web server(同步方法是高级技巧,可以更高效地使用线程池中的线程)。
下面是几种方式进入线程池:
- 通过Task Parallel Library(.NET 4.0)
- 通过调用ThreadPool.QueueUserWorkItem
- 通过asynchronous delegates
- 通过BackgroundWorkder
下面的结构直接使用线程池:
- WCF,Remoting,ASP.NET,ASMX Web Services application servers
- System.Timers.Timer and System.Threading.Timer
- Framework methods 由Async结束,比如WebClient(the event-based asynchronous pattern)和大部分的BeginXXX方法(the asynchronous programming model pattern)
- PLINQ
Task Parallel Library(TPL)和PLINQ是充分有效的和高等级的,甚至当线程池是不重要的时候,你也会想使用它们去协助处理多线程。
现在我们简单的看一下我们怎样使用Task类来实现一个简单的运行在线程池上的委托。
当使用线程池时需要注意下面的事情:
- 你不能设置一个线程的名字,因为设置线程的名字将会使调试更困难(当你在VS线程窗口中调试时,即使你可以附加一个描述)。
- 线程池中的线程总是后台线程(这通常不是问题)。
- 在应用程序的开始期间,阻塞一个线程可能会触发一个延迟,除非你调用ThreadPool.SetMinThreads
你不能任意地改变池中的线程的优先级-因为当它释放会池中的时候,优先级会被还原为正常状态。
你可以通过属性Thread.CurrentThread.IsThreadPoolThread的属性查询线程是否是正在运行的一个池中的线程
二、通过TPL进入线程池
你可以使用在TaskParallel Library中的Task类来轻松的进入线程池。这个Task类在Framework 4.0中有介绍:如果你对老的结构比较熟悉,考虑用非泛型的Task类替换ThreadPool.QueueUserWorkItem,将Asunchoronous delgates替换为泛型Task<TResult>。最新的结构速度更快,更方便,而且更复杂。
为了使用非泛型的任务类,调用Task.Factory.StartNew方法,将方法传进委托中。
Task.Factory.StartNew会返回一个Task对象,你可以使用它去监控这个task,比如,你可以调用它的wait方法等待它直到它完成。
static void Main(string[] args)
{
Task task = Task.Factory.StartNew(Go);
task.Wait();
Console.ReadKey();
} static void Go()
{
Console.WriteLine("From the thread pool start...");
Thread.Sleep(3000);
Console.WriteLine("From the thread pool end");
}
当你调用task的Wait 方法时,一个未处理的异常会很容易地重新抛出到宿主线程上。(如果你不调用Wait方法而是放弃这个task,一个未处理的异常将会关闭掉这个进程)
泛型Task<Tresult>类是非泛型Task的子类。它让你从这个已经完成执行的task中得到一个返回值。在下面的例子中,我们使用Task<TResult>来下载一个web page
static void Main(string[] args)
{
Task<string> task = Task.Factory.StartNew<string>(
() => DownloadString("http://www.baidu.com"));
//调用其他方法
// //可以用task的Result的属性来获得task返回值。
//如果这个任务还在运行,当前的主线程将会被阻塞,直到这个任务完成。
string result = task.Result;
} static string DownloadString(string uri)
{
using(var wc = new System.Net.WebClient())
{
return wc.DownloadString(uri);
}
}
Task Parallel Library有许多的功能,特别是提升多核处理器的性能。我们会在并行编程中继续讨论TPL。
三、不用TPL进入到线程池
如果你的应用程序是.NET Framework的早期版本(4.0之前的版本),你将不能使用TPL。你必须使用老的结构进入线程池:
ThreadPool.QueueUserWorkItem和asynchoronous delegates.两者的不同点是asynchronous delegates让你从线程那里返回数据。Asynchronous delegates收集任何exception返回给调用者。
要使用QueueUserWorkItem,只需调用这个方法的运行在线程池上的委托。
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(Go);
ThreadPool.QueueUserWorkItem(Go, 123);
Console.ReadKey();
} static void Go(object data)
{
Console.WriteLine("A from thread pool! " + data);
}

我们的目标方法Go,必须接收一个简单object类型的参数(为了满足waitCallBack委托)。这将提供一个简单的方式传递数据到方法中,就像是ParameterizedThreadStart。不像Task,QueueUserWorkItem不会返回一个对象去帮助你之后管理执行。还有,你必须显式在目标方法的代码中写处理异常的代码-因为未处理的异常将会终止程序。
ThreadPool.QueueUserWorkItem没有提供从一个已经完成的线程中得到它的返回值的机制。Asynchronous delegate invocations(asynchronous delegates for short)解决了这个问题,允许任何个数类型化的参数在两个方向传递。此外,在asynchronous delegates上未处理的异常很方便地在原始线程上重新抛出(更准确地说,这个线程叫做EndInvoke),因此不需要显示处理。
不要混淆asynchronous delegates和asynchronous method(方法以Begin和End开头的,比如File.BeginRead/File.EndRead)。Asynchronous methods表面上按照简单的协议,但是它们的存在是为了解决一个更困难的问题。
下面是怎样通过一个asynchronous delegate开始一个worker task:
- 实例化一个委托,该委托针对你想要并行运行的method(典型的是预定义Func delegates其中的一种)。
- 在delegate上调用BeginInvoke,保存它的IAsyncResult返回值。BeginInvoke立即返回给调用者。当其他池中的线程正在运行的时候,你可以执行其他动作。
- 当你需要这个结果,在delegate上调用EndInvoke,传递已保存的IAsyncResult对象。
在下面的例子中,我们使用一个asynchronous delegate invocation运行一个与主线程同时运行的简单方法,这个方法返回一个字符串的长度:
static void Main(string[] args)
{
Func<string, int> t = Go;
IAsyncResult result = t.BeginInvoke("test", null, null);
//
// ... 这里可以执行其他并行的任务
// int length = t.EndInvoke(result);
Console.WriteLine("String lenth is: " + length);
Console.ReadKey();
} static int Go(string messsage)
{
return messsage.Length;
}
EndInvoke做三件事情。第一,如果asynchronous delegate没有完成执行,则一直等待它完成。第二,接收返回值(以及任何ref或者out参数)。第三,返回任何未处理的线程异常给调用它的线程。
注意:如果你用asynchronous delegate调用的方法没有返回值,你在技术上需要调用EndInvoke。在实践中,这是开放的辩论;没有Endinvoke报警去管理处罚未编译者!如果你选择不去调用EndInvoke,然而,你需要考虑在线程的异常去避免静默失败。
当你调用BeginInvoke方法时,可以指定一个call back delegate-一个可以接收一个IAsyncResult 对象的方法,它会在委托方法完成后被自动调用这个允许正在发动的线程忘记asynchronous delegate,但它在call back结束时需要一点额外的工作。
线程池的使用的提升还没有写,最近两年作息不规律,程序员得养好身体,早睡早起,睡觉睡觉。希望这篇博客能帮到大家,希望得到园友们的支持!
作 者:
Jackson0714
出 处:http://www.cnblogs.com/jackson0714/
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!
C#多线程之旅(3)——线程池的更多相关文章
- C# 多线程的自动管理(线程池) 基于Task的方式
C# 多线程的自动管理(线程池) 在多线程的程序中,经常会出现两种情况: 1. 应用程序中线程把大部分的时间花费在等待状态,等待某个事件发生,然后给予响应.这一般使用 ThreadPool(线程 ...
- 多线程(七)JDK原生线程池
如同数据库连接一样,线程的创建.切换和销毁同样会耗费大量的系统资源.为了复用创建好的线程,减少频繁创建线程的次数,提高线程利用率可以引用线程池技术.使用线程池的优势有如下几点: 1.保持 ...
- java核心-多线程(6)-线程池-ThreadPoolExecutor
1.java多线程编程少不了使用线程池,线程池相关的工具类所在jdk包,java.util.concurrent 2.使用示例 demo1 public class ThreadPoolDemo { ...
- 第十章:Python高级编程-多线程、多进程和线程池编程
第十章:Python高级编程-多线程.多进程和线程池编程 Python3高级核心技术97讲 笔记 目录 第十章:Python高级编程-多线程.多进程和线程池编程 10.1 Python中的GIL 10 ...
- java多线程详解(7)-线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...
- java多线程总结五:线程池的原理及实现
1.线程池简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创 ...
- java多线程(四)-自定义线程池
当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...
- Linux多线程实践(9) --简单线程池的设计与实现
线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...
- java多线程系列六、线程池
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...
随机推荐
- 【转】JavaScript下对去重算法的优化
本人较懒,直接将链接附上: JavaScript下去重算法优化:http://www.linuxde.net/2013/02/12062.html
- WPF控件经验小结:(1) ToolBar去掉右边箭头(扩展图标)
今天开发时,同事问我一个问题.怎么去除ToolBar右边扩展图标.我想了一下,说改Style.同事说太麻烦了.可不可以快速修正.我说应该动态去读取Template模板中的Style,然后隐藏.怎么实现 ...
- 对bit、byte、TByte、Char、string、进制的认识
在学校老师就教1byte = 8bit,一个Byte在内存中占8个房间.每个房间都有门牌号.找到内存中的内容就找门牌号,寻址什么的,虽然在听,但是脑袋里一头雾水,到现在只知道会用就行,但原理也不是那么 ...
- Go+sublime text3的环境搭建
1.安装Go语言. .msi下载地址:http://download.csdn.net/detail/u014075041/9555543 根据提示安装成功! 在命令行中执行 go env 有提示 ...
- HTML5实战1
第一章 1.搭建环境,wamp 2.检查浏览器是否支持html5 ,是否支持新标签<canvas></canvas> 3.简单高效,少用id,多用标签. 4.使用css3美化样 ...
- "微空间"免费空间很棒哦,很适合中小网站站长
“微空间”(www.idcbt.com)是最长久的免费云主机 现在在站长圈子里面非常火爆的“微空间”免费空间网(www.idcbt.com)犹如火山爆发一样,瞬间成为了广大站长津津乐道的免费主机空间. ...
- 实现Ogre的脚本分离 - 天龙八部的源码分析(一)
目的 在研究天龙八部游戏的源码之时, 发现 Ogre 材质的模板部分被单独放在一个 material 文件之内, 继承模板的其他材质则位于另外的文件, 当我使用Ogre 官方源码, 加载脚本时其不会查 ...
- 微软Connect教程系列—VS2015集成新潮工具(四)
本课程来源与微软connect视频教程,Modern Web Tooling in Visual Studio 2015 本课程主要讲下当下流行的前端工具 bower和grunt 首先简单介绍下这俩货 ...
- 【C语言学习】《C Primer Plus》第8章 字符输入/输出和输入确认
学习总结 1.缓冲区分为完全缓冲区(fully buffered)I/O和行缓冲区(line-buffered)I/O.对完全缓冲输入来说,当缓冲区满的时候会被清空(缓冲区内容发送至其目的地).这类型 ...
- 【吉光片羽】ie6兼容性的几个点
1.浮动换行.自己写个导航,li向左浮动,到ie6下全部错开了. --> 还是在现有bootstrap框架的基础上修改样式保险一些. <div id="mymenu" ...