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

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

 设置线程数量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. C#中利用JQuery实现视频网站的缩略图采集

    最近有朋友想要采集优酷的视频标题和缩略图 (哈哈, 并非商业目的). 找到我帮忙, 考虑到有我刚刚发布的SpiderStudio, 我毫不犹豫的答应了. 首先在网页上视频的基本结构为: div.v - ...

  2. C++中变量做数组长度

    在Java中,这是完全可以的,比如我们运行如下程序: package cn.darrenchan.storm; import java.util.Arrays; public class Test { ...

  3. Nginx + Lua + 共享内存

    转自:http://blog.csdn.net/lxb_champagne/article/details/17099383 lua_package_path "/usr/local/sha ...

  4. CSS浮动与清除浮动(overflow)例子

    在css中浮动与清除浮动功能是我们开发中常用到的一个功能了,下面小编来为各位分析关于CSS浮动与清除浮动(overflow)例子吧. float脱离文本流,可是为什么文字却会有环绕的效果,这点实在是神 ...

  5. javascript 实现java中的Map

    javascript实现java中的Map,代码是在国外的一个网站上看到的(http://stackoverflow.com/questions/368280/javascript-hashmap-e ...

  6. mapreduce程序调用各个类的功能

    转自:http://www.cnblogs.com/z1987/p/5052409.html 1.map类 map类继承了库类中的Mapper,即Mapper<KEYIN, VALUEIN, K ...

  7. 织梦dede模板中广告的去除方法?

    织梦)dede模板中广告的去除方法1.我们先删除头部的广告,找到templetsdefault下的head.htm文件,打开后找到<div>{dede:myad name=’innerTo ...

  8. git fork同步是什么意思?

    这篇文章主要介绍了git fork同步是什么意思?fork到了哪里?有什么用?怎样用?跟clone有什么差别?本文就一一解释这些问题,须要的朋友能够參考下 官方文档:http://help.githu ...

  9. jQuery实现高亮显示网页关键词的方法

    本文实例讲述了jQuery实现高亮显示网页关键词的方法.分享给大家供大家参考.具体如下: 这是一款基于jquery实现的高亮显示网页上搜索关键词的代码,当你在文本框中输入的时候,如果下面的正文中包括你 ...

  10. Maven仓库的搭建

    http://blog.csdn.net/xiao__gui/article/details/52625660 Maven仓库是有特定规则的目录结构. 目录结构由 仓库根目录 , groupId , ...