阅读目录

  • 代码下载
  • 一、介绍
  • 二、通过TPL进入线程池
  • 三、不用TPL进入到线程池

v博客前言

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

v写在前面

多线程,有利也有弊,使用需谨慎。

v正文开始

代码下载

Thread_博客园_cnblogs_jackson0714.zip

第一篇~第三篇的代码示例:

源码地址:https://github.com/Jackson0714/Threads

回到顶部

一、介绍

无论你什么时候开始一个线程,几百毫秒会花在整理一个新的local variable stack。每一个线程默认会消耗1MB的内存。线程池通过分享和回收线程来削减这些开销,允许多线程被应用在一个非常颗粒级的级别而没有性能损失。当充分利用多核系统去执行密集型计算的并行代码时这是非常有用的。

线程池也会在线程的总数量上保持一个限制,从而使线程能够更平稳地运行。太多的线程将会造成管理负担和使CPU缓存是小,从而造成操作系统不能运行。一旦一个限制到达,job排队等待直到另外一个完成才开始。这会使任意的并行应用程序成为可能,比如一个web server(同步方法是高级技巧,可以更高效地使用线程池中的线程)。

下面是几种方式进入线程池:

  1. 通过Task Parallel Library(.NET 4.0)
  2. 通过调用ThreadPool.QueueUserWorkItem
  3. 通过asynchronous delegates
  4. 通过BackgroundWorkder

下面的结构直接使用线程池:

  1. WCF,Remoting,ASP.NET,ASMX Web Services application servers
  2. System.Timers.Timer and System.Threading.Timer
  3. Framework methods 由Async结束,比如WebClient(the event-based asynchronous pattern)和大部分的BeginXXX方法(the asynchronous programming model pattern)
  4. PLINQ

Task Parallel Library(TPL)和PLINQ是充分有效的和高等级的,甚至当线程池是不重要的时候,你也会想使用它们去协助处理多线程。

现在我们简单的看一下我们怎样使用Task类来实现一个简单的运行在线程池上的委托。

当使用线程池时需要注意下面的事情:

  1. 你不能设置一个线程的名字,因为设置线程的名字将会使调试更困难(当你在VS线程窗口中调试时,即使你可以附加一个描述)。
  2. 线程池中的线程总是后台线程(这通常不是问题)。
  3. 在应用程序的开始期间,阻塞一个线程可能会触发一个延迟,除非你调用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方法等待它直到它完成。

1

2

3

4

5

6

7

8

9

10

11

12

13

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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.QueueUserWorkItemasynchoronous delegates.两者的不同点是asynchronous delegates让你从线程那里返回数据。Asynchronous delegates收集任何exception返回给调用者。

要使用QueueUserWorkItem,只需调用这个方法的运行在线程池上的委托。

1

2

3

4

5

6

7

8

9

10

11

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:

  1. 实例化一个委托,该委托针对你想要并行运行的method(典型的是预定义Func delegates其中的一种)。
  2. 在delegate上调用BeginInvoke,保存它的IAsyncResult返回值。BeginInvoke立即返回给调用者。当其他池中的线程正在运行的时候,你可以执行其他动作。
  3. 当你需要这个结果,在delegate上调用EndInvoke,传递已保存的IAsyncResult对象。

在下面的例子中,我们使用一个asynchronous delegate invocation运行一个与主线程同时运行的简单方法,这个方法返回一个字符串的长度:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

static void Main(string[] args)

{

    Func<stringint> t = Go;

    IAsyncResult result = t.BeginInvoke("test"nullnull);

    //

    // ... 这里可以执行其他并行的任务

    //

    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结束时需要一点额外的工作。

v写在最后

线程池的使用的提升还没有写,最近两年作息不规律,程序员得养好身体,早睡早起,睡觉睡觉。希望这篇博客能帮到大家,希望得到园友们的支持!

作  者: Jackson0714 
出  处:http://www.cnblogs.com/jackson0714/ 
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教! 
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。 
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我

C#多线程之旅(3)——线程池相关推荐

  1. C#多线程学习(四) 多线程的自动管理(线程池) (转载系列)——继续搜索引擎研究...

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  2. 多线程编程学习笔记——线程池(二)

    接上文 多线程编程学习笔记--线程池(一) 三.线程池与并行度 此示例是学习如何应用线程池实现大量的操作,及与创建大量线程进行工作的区别. 1. 代码如下 using System; using Sy ...

  3. 多线程编程定长线程池

    多线程编程 定长线程池,可控制线程最大并发数,超出的线程会在队列中等待 Executors的方式创建定长线程池(不推荐容易,容易内存溢出OOM) ThreadPoolExecutor构造函数创建定长线 ...

  4. 【python爬虫学习记录 持续更新】多线程多进程,带线程池爬取实例

    文章目录 简介 多线程codingFrame 多进程codingFrame 线程池与进程池 线程池爬取实例(主页url隐了 主要看思路 和如何使用线程池框架) 简介 进程是资源单位 线程是执行单位 每 ...

  5. gj11 多线程、多进程和线程池编程

    11.1 python中的GIL # coding=utf-8 # gil global interpreter lock (cpython) # python中一个线程对应于c语言中的一个线程 # ...

  6. [转]C#多线程学习(四) 多线程的自动管理(线程池)

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  7. java多线程总结五:线程池的原理及实现

    1.线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.         假设一个服务器完成一项任务所需时间为:T1 ...

  8. boost创建线程池_Java并发 之 线程池系列 (1) 让多线程不再坑爹的线程池

    目录 背景 线程池的来由 什么是线程池 背景总结 用法 通过Executors创建线程池 Executors及其服务的类 Executors常用的几个方法 一个线程池的例子 任务 池子 测试 说明 总 ...

  9. terminated 线程_Java【多线程系列】JUC线程池—2. 原理(二)、Callable和Future

    在"Java多线程系列--"基础篇"01之 基本概念"中,我们介绍过,线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态:然而 ...

最新文章

  1. JQuery 给DOM控件绑定和移除事件的方法
  2. sessionStorage 、localStorage 和 cookie 之间的区别
  3. word中复制、双击打开编辑公式(Axmath/mathtype)出现卡死(无响应)现象的解决方案
  4. Windows 7 shortcut icons missing (快捷方式图标丢失)
  5. Hadoop中shuffle阶段流程分析
  6. 一次彻底搞透协议设计(没做过通讯底层也没有关系)!
  7. boost::polygon模块实现自定义点相关的测试程序
  8. TCP三次握手、四次挥手、socket,tcp,http三者之间的区别和原理
  9. java integer valueof_一文读懂什么是Java中的自动拆装箱
  10. [编程启蒙游戏] 1. 猜数字
  11. backup ram不稳定 stm32_具备无线能力的STM32,如何让智能手表更加炙手可热?
  12. Python批量Excel文件数据导入SQLite数据库的优化方案
  13. Initramfs应用问题记录
  14. [转]RamDisk导致远程桌面客户端无法启动问题
  15. 免费公共DNS服务器大全
  16. Android仿芝麻信用分雷达分布图
  17. iOS面试题-UI篇
  18. 压力传感器与压力变送器的区别
  19. 计算机考试用到的英语词汇,BEC商务英语_计算机英语高级词汇·软件篇_沪江英语...
  20. 诸葛io的技术架构图_大数据平台的三次浪潮和诸葛io自身架构演变历程 (3)

热门文章

  1. mysql六:数据备份、pymysql模块
  2. 根据另外一个表来更新,增加字段
  3. 《avascript 高级程序设计(第三版)》 ---第三章 基本概念
  4. eclipse下创建Maven项目
  5. WPF指南之XAML概述
  6. Depth-first Search深度优先搜索专题6
  7. [Leedcode][JAVA][第470题][Ran7()实现Rand10()]
  8. python关机linux_Python学习第157课——Linux切换用户、关机、查看正在运行的程序...
  9. python 获取字符串中的字典_python cookies提取——从字符串到字典(一行Python代码)...
  10. java excel类库,jExcelApi Java 操作 Excel 的类库