线程池可以看做容纳线程的容器;

一个应用程序最多只能有一个线程池;

 设置线程数量ThreadPool.SetMaxThreads(initDownCardThreadPool, maxDownCardThreadPool)

ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池;

每排入一个工作函数,就相当于请求创建一个线程;

线程池的作用:

线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。

如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。

什么时候使用ThreadPool?

ThreadPool 示例一 :

  1. ThreadPool_1.csCode highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->using System;
  2. using System.Text;
  3. using System.Threading;
  4. namespace 多线程
  5. {
  6. public class Example
  7. {
  8. public static void Main()
  9. {
  10. // Queue the task.
  11. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
  12. Console.WriteLine("Main thread does some work, then sleeps.");
  13. Thread.Sleep(1000);
  14. Console.WriteLine("Main thread exits.");
  15. }
  16. static void ThreadProc(Object stateInfo)
  17. {
  18. // No state object was passed to QueueUserWorkItem,
  19. // so stateInfo is null.
  20. Console.WriteLine("Hello from the thread pool.");
  21. }
  22. }
  23. }

ThreadPool 示例二 :

  1. ThreadPool_2.csCode highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Threading;
  5. namespace CS_Test
  6. {
  7. class ThreadPool_Demo
  8. {
  9. // 用于保存每个线程的计算结果
  10. static int[] result = new int[10];
  11. //注意:由于WaitCallback委托的声明带有参数,
  12. //      所以将被调用的Fun方法必须带有参数,即:Fun(object obj)。
  13. static void Fun(object obj)
  14. {
  15. int n = (int)obj;
  16. //计算阶乘
  17. int fac = 1;
  18. for (int i = 1; i <= n; i++)
  19. {
  20. fac *= i;
  21. }
  22. //保存结果
  23. result[n] = fac;
  24. }
  25. static void Main(string[] args)
  26. {
  27. //向线程池中排入9个工作线程
  28. for (int i = 1; i <= 9 ; i++)
  29. {
  30. //QueueUserWorkItem()方法:将工作任务排入线程池。
  31. ThreadPool.QueueUserWorkItem(new WaitCallback(Fun),i);
  32. // Fun 表示要执行的方法(与WaitCallback委托的声明必须一致)。
  33. // i   为传递给Fun方法的参数(obj将接受)。
  34. }
  35. //输出计算结果
  36. for (int i = 1; i <= 9; i++)
  37. {
  38. Console.WriteLine("线程{0}: {0}! = {1}",i,result[i]);
  39. }
  40. }
  41. }
  42. }

ThreadPool的作用:

解决多线程编程中大并发数等待唤醒的问题

在移动交通流调查项目的一个算法分析程序中,碰到一个业务问题:用户采集上传的基站定位数据需要进行分析预处理,方案是先按预定格式解析文件并从中提取出成百上千个基站定位数据记录,并合并相同的基站点,根据获取到的基站位置信息作为参数,去请求google 基站定位 api,从而得到对应的基站定位经纬度等信息,接下来再加上华工的算法分析。

在执行华工算法分析逻辑之前,调用谷歌api这一步必需全部完成;网络请求是个耗时的过程,故对每一个请求开启单独的线程(同时请求可能数百个,这里通过Semaphore信号量来控制每次发出请求的最大数,该部分的讨论不再本话题之类)。

原理:封装一个ManualResetEvent对象,一个计数器current,提供SetOne和WaitAll方法;

主线程调用WaitAll方法使ManualResetEvent对象等待唤醒信号;

各个子线程调用setOne方法 ,setOne每执行一次current减1,直到current等于0时表示所有子线程执行完毕 ,调用ManualResetEvent的set方法,这时主线程可以执行WaitAll之后的步骤。

目标:减少ManualResetEvent对象的大量产生和使用的简单性。

在这里我写了个封装类:

  1. /********************************************************************************
  2. * Copyright © 2001 - 2010Comit. All Rights Reserved.
  3. * 文件:MutipleThreadResetEvent.cs
  4. * 作者:杨柳
  5. * 日期:2010年11月13日
  6. * 描述:封装 ManualResetEvent ,该类允许一次等待N(N>64)个事件执行完毕
  7. *
  8. *       解决问题:WaitHandle.WaitAll(evetlist)方法最大只能等待64个ManualResetEvent事件
  9. * *********************************************************************************/
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading;
  15. namespace TestMutipleThreadRestEvent
  16. {
  17. /// <summary>
  18. ///  封装ManualResetEvent
  19. /// </summary>
  20. public class MutipleThreadResetEvent : IDisposable
  21. {
  22. private readonly ManualResetEvent done;
  23. private readonly int total;
  24. private long current;
  25. /// <summary>
  26. /// 构造函数
  27. /// </summary>
  28. /// <param name="total">需要等待执行的线程总数</param>
  29. public MutipleThreadResetEvent(int total)
  30. {
  31. this.total = total;
  32. current = total;
  33. done = new ManualResetEvent(false);//done初始为非终止状态,有效状态,可以阻塞当前进程
  34. }
  35. /// <summary>
  36. /// 唤醒一个等待的线程
  37. /// </summary>
  38. public void SetOne()
  39. {
  40. // Interlocked 原子操作类 ,此处将计数器减1
  41. if (Interlocked.Decrement(ref current) == 0)
  42. {
  43. //当所以等待线程执行完毕时,唤醒等待的线程
  44. done.Set();
  45. }
  46. }
  47. /// <summary>
  48. /// 等待所以线程执行完毕
  49. /// </summary>
  50. public void WaitAll()
  51. {
  52. done.WaitOne();
  53. }
  54. /// <summary>
  55. /// 释放对象占用的空间
  56. /// </summary>
  57. public void Dispose()
  58. {
  59. ((IDisposable)done).Dispose();
  60. }
  61. }
  62. }

注释写的很清楚了:本质就是只通过1个ManualResetEvent 对象就可以实现同步N(N可以大于64)个线程

下面是测试用例:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. namespace TestMutipleThreadRestEvent
  7. {
  8. /// <summary>
  9. /// 测试MutipleThreadResetEvent
  10. /// </summary>
  11. class Program
  12. {
  13. static int i = 0;
  14. /// <summary>
  15. /// 主方法
  16. /// </summary>
  17. /// <param name="args">参数</param>
  18. static void Main(string[] args)
  19. {
  20. //假设有100个请求线程
  21. int num = 100;
  22. //使用 MutipleThreadResetEvent
  23. using (var countdown = new MutipleThreadResetEvent(num))
  24. {
  25. for (int i=0;i<num;i++)
  26. {
  27. //开启N个线程,传递MutipleThreadResetEvent对象给子线程
  28. ThreadPool.QueueUserWorkItem(MyHttpRequest, countdown);//countdown为MyHttpRequest进入点函数提供参数
  29. }
  30. //等待所有线程执行完毕
  31. countdown.WaitAll();//主线程阻塞,直至处理完所有请求后,将<span style="font-family: 'ms shell dlg';">ManualResetEvent.Set(),置为终止状态,主线程可以运行</span>
  32. }
  33. Console.WriteLine("所有的网络请求以及完毕,可以继续下面的分析...");
  34. Console.ReadKey();
  35. }
  36. /// <summary>
  37. /// 假设的网络请求
  38. /// </summary>
  39. /// <param name="state">参数</param>
  40. private static void MyHttpRequest(object state)//state为加入进程池时传给<span style="font-family: 'ms shell dlg';">MyHttpRequest的参数countdown</span>
  41. {
  42. // Thread.Sleep(1000);
  43. Console.WriteLine(String.Format("哈哈:{0}",++i));
  44. MutipleThreadResetEvent countdown = state as MutipleThreadResetEvent;
  45. //发送信号量 本线程执行完毕
  46. countdown.SetOne();//只有当所有请求处理完后,count=0,才会将<span style="font-family: 'ms shell dlg';">ManualResetEvent置为终止状态</span>
  47. }
  48. }
  49. }

输出:

      …  省略 ...   

从结果上看线程执行的完成的时间顺序是不固定的;并且只有在所有100个网络请求任务完成后,才显示可以继续下面的分析。

与上面的方案是一样的效果,但是本方案使用非常简单,出错的概念小,免去了创建大量 ManualResetEvent 对象的烦恼

该解决方案可以适用与.net framework 2.0 以上的运行时。

tips:在.net framework 4.0 中有一个CountdownEvent对象可以实现类似的功能;

不过目前公司大多数项目运行时还是基于.net framework 2.0 和 3.5

注:ManualResetEvent详解

ManualResetEvent 允许线程通过发信号互相通信。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态,此线程可被视为控制 ManualResetEvent。调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。当控制线程完成活动时,它调用 Set 以发出等待线程可以继续进行的信号。并释放所有等待线程。一旦它被终止,ManualResetEvent 将保持终止状态(即对 WaitOne 的调用的线程将立即返回,并不阻塞),直到它被手动重置。可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading;
    6. namespace ManualResetEventDemo
    7. {
    8. class MREDemo
    9. {
    10. private ManualResetEvent _mre;
    11. public MREDemo()
    12. {
    13. this._mre = new ManualResetEvent(true);
    14. }
    15. public void CreateThreads()
    16. {
    17. Thread t1 = new Thread(new ThreadStart(Run));
    18. t1.Start();
    19. Thread t2 = new Thread(new ThreadStart(Run));
    20. t2.Start();
    21. }
    22. public void Set()
    23. {
    24. this._mre.Set();
    25. }
    26. public void Reset()
    27. {
    28. this._mre.Reset();
    29. }
    30. private void Run()
    31. {
    32. string strThreadID = string.Empty;
    33. try
    34. {
    35. while (true)
    36. {
    37. // 阻塞当前线程
    38. this._mre.WaitOne();
    39. strThreadID = Thread.CurrentThread.ManagedThreadId.ToString();
    40. Console.WriteLine("Thread(" + strThreadID + ") is running...");
    41. Thread.Sleep(5000);
    42. }
    43. }
    44. catch(Exception ex)
    45. {
    46. Console.WriteLine("线程(" + strThreadID + ")发生异常!错误描述:" + ex.Message.ToString());
    47. }
    48. }
    49. }
    50. }
    51. using System;
    52. using System.Collections.Generic;
    53. using System.Linq;
    54. using System.Text;
    55. namespace ManualResetEventDemo
    56. {
    57. class Program
    58. {
    59. static void Main(string[] args)
    60. {
    61. Console.WriteLine("****************************");
    62. Console.WriteLine("输入\"stop\"停止线程运行...");
    63. Console.WriteLine("输入\"run\"开启线程运行...");
    64. Console.WriteLine("****************************\r\n");
    65. MREDemo objMRE = new MREDemo();
    66. objMRE.CreateThreads();
    67. while (true)
    68. {
    69. string input = Console.ReadLine();
    70. if (input.Trim().ToLower() == "stop")
    71. {
    72. Console.WriteLine("线程已停止运行...");
    73. objMRE.Reset();
    74. }
    75. else if (input.Trim().ToLower() == "run")
    76. {
    77. Console.WriteLine("线程开启运行...");
    78. objMRE.Set();
    79. }
    80. }
    81. }
    82. }
    83. }

C#线程池ThreadPool的更多相关文章

  1. 线程池ThreadPool的初探

    一.线程池的适用范围 在日常使用多线程开发的时候,一般都构造一个Thread示例,然后调用Start使之执行.如果一个线程它大部分时间花费在等待某个事件响应的发生然后才予以响应:或者如果在一定期间内重 ...

  2. C#多线程学习 之 线程池[ThreadPool](转)

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

  3. 高效线程池(threadpool)的实现

    高效线程池(threadpool)的实现 Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线 ...

  4. 多线程系列 线程池ThreadPool

    上一篇文章我们总结了多线程最基础的知识点Thread,我们知道了如何开启一个新的异步线程去做一些事情.可是当我们要开启很多线程的时候,如果仍然使用Thread我们需要去管理每一个线程的启动,挂起和终止 ...

  5. C# -- 使用线程池 ThreadPool 执行多线程任务

    C# -- 使用线程池 ThreadPool 执行多线程任务 1. 使用线程池 class Program { static void Main(string[] args) { WaitCallba ...

  6. 多线程Thread,线程池ThreadPool

    首先我们先增加一个公用方法DoSomethingLong(string name),这个方法下面的举例中都有可能用到 #region Private Method /// <summary> ...

  7. C# 线程池ThreadPool的用法简析

    https://blog.csdn.net/smooth_tailor/article/details/52460566 什么是线程池?为什么要用线程池?怎么用线程池? 1. 什么是线程池? .NET ...

  8. 多线程系列(2)线程池ThreadPool

    上一篇文章我们总结了多线程最基础的知识点Thread,我们知道了如何开启一个新的异步线程去做一些事情.可是当我们要开启很多线程的时候,如果仍然使用Thread我们需要去管理每一个线程的启动,挂起和终止 ...

  9. C#多线程学习 之 线程池[ThreadPool]

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

  10. 线程池ThreadPool的常用方法介绍

    线程池ThreadPool的常用方法介绍 如果您理解了线程池目的及优点后,让我们温故下线程池的常用的几个方法: 1. public static Boolean QueueUserWorkItem(W ...

随机推荐

  1. Android学习之两款下拉刷新库分享

    昨天没有写博客.心里非常罪过呀,今天给大家写两种比較常见的下拉刷新的用法.一款是SwipeRefreshLayout,一款是CircleRefreshLayout. SwipeRefreshLayou ...

  2. Lua中的table函数库

    table.concat(table, sep,  start, end) concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组 ...

  3. Spring Boot 属性配置&自定义属性配置

    在使用spring boot过程中,可以发现项目中只需要极少的配置就能完成相应的功能,这归功于spring boot中的模块化配置,在pom.xml中依赖的每个Starter都有默认配置,而这些默认配 ...

  4. 在函数体的“出口处”,对 return 语句的正确性和效率进行检查

    在函数体的“出口处”,对 return 语句的正确性和效率进行检查. 如果函数有返回值,那么函数的“出口处”是 return 语句. 我们不要轻视 return 语 句.如果 return 语句写得不 ...

  5. JAVA 并发编程-多个线程之间共享数据(六)

    多线程共享数据的方式: 1.假设每一个线程运行的代码同样.能够使用同一个Runnable对象,这个Runnable对象中有那个共享数据,比如,卖票系统就能够这么做. 2,假设每一个线程运行的代码不同. ...

  6. RabbitMQ用户角色及权限控制 -2

    1.RabbitMQ的用户角色分类: none.management.policymaker.monitoring.administrator none 不能访问 management plugin ...

  7. 理解Loadrunner中的Browser Emulation Simulate

    案例 测试环境描述: 客户端 5台 Windows2000机器.服务器端  20台机器 一台F5(负载均衡设备,提供一个唯一的IP供客户端访问) 客户端绑定Host后,使用域名http://www.* ...

  8. Math函数

    floor --将一个小数向下舍入为整数 float floor ( float $value ) 注意:floor返回的虽然是取整的数字 但是类型仍然是float类型. 实例: echo floor ...

  9. Socket长连接和短连接的区别

    https://blog.csdn.net/jasonjwl/article/details/52085264 短连接 连接->传输数据->关闭连接 HTTP是无状态的,浏览器和服务器每进 ...

  10. python真值表

    author:headsen chen  date :2018-06-01  10:53:39 notice:not allowed to copy or you will count law que ...