本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/ThreadPool.html,记录一下学习过程以备后续查用。

、线程池基础

首先,创建和销毁线程是一个要耗费大量时间的过程,其次,太多的线程也会浪费内存资源,所以通过Thread类来创建过多的线程反而有损于性能。为了改善这样

的问题 ,.NET中就引入了线程池。

线程池形象的表示就是存放应用程序中使用的线程的一个集合(就是放线程的地方,这样线程都放在一个地方就好管理了)。

CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列。当应用程序想执行一个异步操作时,就调用一个方法,将一个任务放到线程池

的队列中,线程池代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另

一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。

MSDN表述:

“线程池经常用在服务器应用程序中,每一个新进来的需求被分配给一个线程池中的线程,这样该需求能被异步的执行,没有阻碍主线程或推迟后继需求的处理。”

    注意:通过线程池创建的线程默认为后台线程,优先级默认为Normal。

    二、通过线程池的工作者线程实现异步

2.1创建工作者线程的方法

public static bool QueueUserWorkItem (WaitCallback callback);

public static bool QueueUserWorkItem(WaitCallback callback, Object state);

这两个方法向线程池的队列添加一个工作项(work item)以及一个可选的状态数据,然后,这两个方法就会立即返回。

工作项其实就是由callback参数标识的一个方法,该方法将由线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型,定义为:

public delegate void WaitCallback(Object state);

下面演示如何通过线程池线程来实现异步调用:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. #region 通过线程池的工作者线程实现异步
  6. //设置线程池中工作者线程最大数量为1000,I/O线程最大数量为1000。
  7. ThreadPool.SetMaxThreads(, );
  8. Console.WriteLine("Main thread: queue an asynchronous method.");
  9. PrintMessage("Main thread start.");
  10.  
  11. //把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法。
  12. ThreadPool.QueueUserWorkItem(AsyncMethod);
  13. Console.Read();
  14. #endregion
  15. }
  16.  
  17. /// <summary>
  18. /// 打印线程池信息
  19. /// </summary>
  20. /// <param name="data"></param>
  21. private static void PrintMessage(string data)
  22. {
  23. //获得线程池中可用的工作者线程数量及I/O线程数量
  24. ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);
  25.  
  26. Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
  27. data,
  28. Thread.CurrentThread.ManagedThreadId,
  29. Thread.CurrentThread.IsBackground.ToString(),
  30. workThreadNumber.ToString(),
  31. ioThreadNumber.ToString());
  32. }
  33.  
  34. /// <summary>
  35. /// 异步方法:必须匹配WaitCallback委托
  36. /// </summary>
  37. /// <param name="state"></param>
  38. private static void AsyncMethod(object state)
  39. {
  40. Thread.Sleep();
  41. PrintMessage("Asynchoronous method.");
  42. Console.WriteLine("Asynchoronous thread has worked.");
  43. }
  44. }

运行结果如下:

从结果中可以看出,线程池中的可用的工作者线程少了一个,用去执行回调方法了。

ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object对象作为参数传送到回调函数中,使用方法与

ThreadPool.QueueUserWorkItem(WaitCallback callback)类似,这里就不列出了。

    2.2 协作式取消

.NET Framework提供了取消操作的模式, 这个模式是协作式的。为了取消一个操作,首先必须创建一个System.Threading.CancellationTokenSource对象。

下面代码演示协作式取消的使用,主要实现当用户在控制台敲下回车键后就停止数数方法。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. #region 协作式取消
  6. ThreadPool.SetMaxThreads(, );
  7. Console.WriteLine("Main thread run.");
  8. PrintMessage("Start");
  9. Run();
  10. Console.ReadKey();
  11. #endregion
  12. }
  13.  
  14. /// <summary>
  15. /// 打印线程池信息
  16. /// </summary>
  17. /// <param name="data"></param>
  18. private static void PrintMessage(string data)
  19. {
  20. //获得线程池中可用的工作者线程数量及I/O线程数量
  21. ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);
  22.  
  23. Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
  24. data,
  25. Thread.CurrentThread.ManagedThreadId,
  26. Thread.CurrentThread.IsBackground.ToString(),
  27. workThreadNumber.ToString(),
  28. ioThreadNumber.ToString());
  29. }
  30.  
  31. /// <summary>
  32. /// 运行工作者线程(包含协作式取消)
  33. /// </summary>
  34. private static void Run()
  35. {
  36. CancellationTokenSource cts = new CancellationTokenSource();
  37.  
  38. //这里是用Lambda表达式的写法,效果一样。
  39. //ThreadPool.QueueUserWorkItem(obj => Count(cts.Token, 1000));
  40.  
  41. ThreadPool.QueueUserWorkItem(Callback, cts.Token);
  42. Console.WriteLine("Press enter key to cancel the operation.\n");
  43. Console.ReadLine();
  44. //传达取消请求
  45. cts.Cancel();
  46. }
  47.  
  48. /// <summary>
  49. /// 回调函数
  50. /// </summary>
  51. /// <param name="state"></param>
  52. private static void Callback(object state)
  53. {
  54. Thread.Sleep();
  55. PrintMessage("Asynchoronous method start.");
  56. CancellationToken token = (CancellationToken)state;
  57. Count(token, );
  58. }
  59.  
  60. /// <summary>
  61. /// 数数
  62. /// </summary>
  63. /// <param name="token"></param>
  64. /// <param name="countTo"></param>
  65. private static void Count(CancellationToken token, int countTo)
  66. {
  67. for (int i = 1; i <= countTo; i++)
  68. {
  69. if (token.IsCancellationRequested)
  70. {
  71. Console.WriteLine("Count is canceled.");
  72. break;
  73. }
  74.  
  75. Console.WriteLine(i);
  76. Thread.Sleep();
  77. }
  78. Console.WriteLine("Count has done.");
  79. }
  80. }

运行结果如下:

、使用委托实现异步

    涉及术语解释--异步编程模型:

APM 异步编程模型(Asynchronous Programming Model)

EAP 基于事件的异步编程模式(Event-based Asynchronous Pattern)

TAP 基于任务的异步编程模式(Task-based Asynchronous Pattern)

通过调用ThreadPool的QueueUserWorkItem方法来来启动工作者线程非常方便,但委托WaitCallback指向的是带有一个参数的无返回值的方法。如果我们实际操作中

需要有返回值,或者需要带有多个参数, 这时通过这样的方式就难以实现了。 为了解决这样的问题,我们可以通过委托来建立工作这线程。

下面代码演示使用委托实现异步:

  1. class Program
  2. {
  3. //使用委托实现异步,是使用了异步编程模型APM。
  4. private delegate string ThreadDelegate();
  5.  
  6. static void Main(string[] args)
  7. {
  8. #region 使用委托实现异步
  9. ThreadPool.SetMaxThreads(, );
  10. PrintMessage("Main thread start.");
  11.  
  12. //实例化委托
  13. ThreadDelegate threadDelegate = new ThreadDelegate(AsyncMethod);
  14. //异步调用委托
  15. IAsyncResult result = threadDelegate.BeginInvoke(null, null);
  16. //获取结果并打印
  17. string returnData = threadDelegate.EndInvoke(result);
  18. Console.WriteLine(returnData);
  19. Console.ReadLine();
  20. #endregion
  21. }
  22.  
  23. /// <summary>
  24. /// 打印线程池信息
  25. /// </summary>
  26. /// <param name="data"></param>
  27. private static void PrintMessage(string data)
  28. {
  29. //获得线程池中可用的工作者线程数量及I/O线程数量
  30. ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);
  31.  
  32. Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
  33. data,
  34. Thread.CurrentThread.ManagedThreadId,
  35. Thread.CurrentThread.IsBackground.ToString(),
  36. workThreadNumber.ToString(),
  37. ioThreadNumber.ToString());
  38. }
  39.  
  40. /// <summary>
  41. /// 异步方法
  42. /// </summary>
  43. /// <returns></returns>
  44. private static string AsyncMethod()
  45. {
  46. Thread.Sleep();
  47. PrintMessage("Asynchoronous method.");
  48. return "Method has completed.";
  49. }
  50. }

运行结果如下:

    四、任务

同样,任务的引入也是为了解决通过ThreadPool.QueueUserWorkItem中限制的问题。

    4.1 使用任务来实现异步

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. #region 使用任务实现异步
  6. ThreadPool.SetMaxThreads(, );
  7. PrintMessage("Main thread start.");
  8. //调用构造函数创建Task对象
  9. Task<int> task = new Task<int>(n => AsyncMethod((int)n), );
  10.  
  11. //启动任务
  12. task.Start();
  13. //等待任务完成
  14. task.Wait();
  15. Console.WriteLine("The method result is: " + task.Result);
  16. Console.ReadLine();
  17. #endregion
  18. }
  19.  
  20. /// <summary>
  21. /// 打印线程池信息
  22. /// </summary>
  23. /// <param name="data"></param>
  24. private static void PrintMessage(string data)
  25. {
  26. //获得线程池中可用的工作者线程数量及I/O线程数量
  27. ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);
  28.  
  29. Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
  30. data,
  31. Thread.CurrentThread.ManagedThreadId,
  32. Thread.CurrentThread.IsBackground.ToString(),
  33. workThreadNumber.ToString(),
  34. ioThreadNumber.ToString());
  35. }
  36.  
  37. /// <summary>
  38. /// 异步方法
  39. /// </summary>
  40. /// <param name="n"></param>
  41. /// <returns></returns>
  42. private static int AsyncMethod(int n)
  43. {
  44. Thread.Sleep();
  45. PrintMessage("Asynchoronous method.");
  46.  
  47. int sum = ;
  48. for (int i = ; i < n; i++)
  49. {
  50. //运算溢出检查
  51. checked
  52. {
  53. sum += i;
  54. }
  55. }
  56.  
  57. return sum;
  58. }
  59. }

运行结果如下:

    4.2 取消任务

如果要取消任务, 同样也可以CancellationTokenSource对象来取消。

下面代码演示取消一个任务:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. #region 取消任务
  6. ThreadPool.SetMaxThreads(, );
  7. PrintMessage("Main thread start.");
  8. CancellationTokenSource cts = new CancellationTokenSource();
  9.  
  10. //调用构造函数创建Task对象,将一个CancellationToken传给Task构造器从而使Task和CancellationToken关联起来。
  11. Task<int> task = new Task<int>(n => AsyncMethod(cts.Token, (int)n), );
  12.  
  13. //启动任务
  14. task.Start();
  15. //延迟取消任务
  16. Thread.Sleep();
  17.  
  18. //取消任务
  19. cts.Cancel();
  20. Console.WriteLine("The method result is: " + task.Result);
  21. Console.ReadLine();
  22. #endregion
  23. }
  24.  
  25. /// <summary>
  26. /// 打印线程池信息
  27. /// </summary>
  28. /// <param name="data"></param>
  29. private static void PrintMessage(string data)
  30. {
  31. //获得线程池中可用的工作者线程数量及I/O线程数量
  32. ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);
  33.  
  34. Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
  35. data,
  36. Thread.CurrentThread.ManagedThreadId,
  37. Thread.CurrentThread.IsBackground.ToString(),
  38. workThreadNumber.ToString(),
  39. ioThreadNumber.ToString());
  40. }
  41.  
  42. /// <summary>
  43. /// 异步方法
  44. /// </summary>
  45. /// <param name="ct"></param>
  46. /// <param name="n"></param>
  47. /// <returns></returns>
  48. private static int AsyncMethod(CancellationToken ct, int n)
  49. {
  50. Thread.Sleep();
  51. PrintMessage("Asynchoronous method.");
  52.  
  53. int sum = ;
  54. try
  55. {
  56. for (int i = ; i < n; i++)
  57. {
  58. //当CancellationTokenSource对象调用Cancel方法时,就会引起OperationCanceledException异常,
  59. //通过调用CancellationToken的ThrowIfCancellationRequested方法来定时检查操作是否已经取消,
  60. //这个方法和CancellationToken的IsCancellationRequested属性类似。
  61. ct.ThrowIfCancellationRequested();
  62. Thread.Sleep();
  63. //运算溢出检查
  64. checked
  65. {
  66. sum += i;
  67. }
  68. }
  69. }
  70. catch (Exception e)
  71. {
  72. Console.WriteLine("Exception is:" + e.GetType().Name);
  73. Console.WriteLine("Operation is canceled.");
  74. }
  75.  
  76. return sum;
  77. }
  78. }

运算结果如下:

4.3 使用任务工厂实现异步操作

同样也可以通过任务工厂TaskFactory类型来实现异步操作。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. #region 使用任务工厂实现异步
  6. ThreadPool.SetMaxThreads(, );
  7. Task.Factory.StartNew(() => PrintMessage("Main thread."));
  8. Console.Read();
  9. #endregion
  10. }
  11.  
  12. /// <summary>
  13. /// 打印线程池信息
  14. /// </summary>
  15. /// <param name="data"></param>
  16. private static void PrintMessage(string data)
  17. {
  18. //获得线程池中可用的工作者线程数量及I/O线程数量
  19. ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);
  20.  
  21. Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
  22. data,
  23. Thread.CurrentThread.ManagedThreadId,
  24. Thread.CurrentThread.IsBackground.ToString(),
  25. workThreadNumber.ToString(),
  26. ioThreadNumber.ToString());
  27. }
  28. }

运行结果如下:

 

C#线程学习笔记二:线程池中的工作者线程的更多相关文章

  1. Java学习笔记二十:Java中的内部类

    Java中的内部类 一:什么是内部类: (1).什么是内部类呢? 内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. (2).那为什么要将一个类定 ...

  2. Java学习笔记二十七:Java中的抽象类

    Java中的抽象类 一:Java抽象类: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就 ...

  3. CodeIgniter学习笔记二:CI中的query_builder(AR)、连贯操作

    一.开启query_builder 在application\config\database.php中添加如下代码(默认已开启): $query_builder = TRUE; 二.查询数据 //ge ...

  4. 【Intel AF 2.1 学习笔记二】AF中的页面——Panel

    Panel Panel控件是你的app中的独立内容的区域控件.它是af UI的核心.Panel div 元素实际上承载了app中你管理和显示的界面元素和内容. 创建panel控件是相当地容易的:在id ...

  5. MyBatis学习笔记二:MyBatis生产中使用环境搭建

    这里是在上一个环境的基础上修改的,这里就不在给出所有的配置,只给出哪里修改的配置 1.修改POJO对象为注解方式 2.创建Dao层接口 package com.orange.dao; import c ...

  6. Linux进程线程学习笔记:运行新程序

    Linux进程线程学习笔记:运行新程序                                         周银辉 在上一篇中我们说到,当启动一个新进程以后,新进程会复制父进程的大部份上下 ...

  7. C#线程学习笔记九:async & await入门二

    一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...

  8. 并发编程学习笔记(3)----synchronized关键字以及单例模式与线程安全问题

    再说synchronized关键字之前,我们首先先小小的了解一个概念-内置锁. 什么是内置锁? 在java中,每个java对象都可以用作synchronized关键字的锁,这些锁就被称为内置锁,每个对 ...

  9. 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法

    [源码下载] 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法 作者:webabcd 介绍重新想象 Wi ...

随机推荐

  1. Thinkphp5——数据库表名的大小写问题

    ThinkPHP5中数据库的表名如果是驼峰命名法,会被转换成小写加下划线,解决方法如下: 1.表名全部小写,因为数据库的表名区分大小写的. 2.使用Db::table("表名"), ...

  2. F#周报2019年第49期

    新闻 宣告.NET Core 3.1 新书:Kevin Avignon的F#提升效率 .NET Core 2.2将在2019年12月23日迎来终结 Visual Studio 16.5预览版1中升级了 ...

  3. Vue+ElementUI项目使用webpack输出MPA【华为云分享】

    [摘要] Vue+ElementUI多页面打包改造 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目 ...

  4. 一条数据的HBase之旅,简明HBase入门教程3:适用场景

    [摘要] 这篇文章继HBase数据模型之后,介绍HBase的适用场景,以及与一些关键场景有关的周边技术生态,最后给出了本文的示例数据 华为云上的NoSQL数据库服务CloudTable,基于Apach ...

  5. hibernate查询方式(四)

    ---恢复内容开始--- 1.mysql中的多表联合查询 ****/*内连接查询*/  只显示两个表有关联的记录 //第一种 SELECT * FROM Class c ,Student s WHER ...

  6. BZOJ1002 [FJOI2007]轮状病毒(最小生成树计数)

    Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 7125  Solved: 3878[Submit][Status][Discuss] Descripti ...

  7. java面向对象基础知识

    一.面向对象与面向过程的区别: ①所处的角色不同:前者指挥者,后者执行者:②所需知道的事情不同:前者知道哪些是做这些事情的人就可以,不需了解具体的事情操作的过程.后者需要具备应有的技能来做这些事情. ...

  8. 5-- String 、StringBulid 、StringBuffer的区别

    String是典型的Immutable(不可变)类,被声明为final class,所有属性都是final的.由于它的不可变性,类似拼接.截取字符串等操作都会产生新的String对象,往往编码中常常对 ...

  9. Socket与系统调用深度分析

    学习一下对Socket与系统调用的分析分析 一.介绍 我们都知道高级语言的网络编程最终的实现都是调用了系统的Socket API编程接口,在操作系统提供的socket系统接口之上可以建立不同端口之间的 ...

  10. C# WPF实用的注册窗体

    时间如流水,只能流去不流回! 点赞再看,养成习惯,这是您给我创作的动力! 本文 Dotnet9 https://dotnet9.com 已收录,站长乐于分享dotnet相关技术,比如Winform.W ...