---来自选择自 AloneSword 的 Blog  

在多线程的程序中,经常会出现两种情况。一种情况下,应用程序中的线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应;而另外一种情况则是线程平常都处于休眠状态,只是周期性地被唤醒。在.net framework里边,我们使用ThreadPool来对付第一种情况,使用Timer来对付第二种情况。

  ThreadPool类提供一个由系统维护的线程池——可以看作一个线程的容器,该容器需要Windows 2000以上版本的系统支持,因为其中某些方法调用了只有高版本的Windows才有的API函数。你可以使用ThreadPool.QueueUserWorkItem()方法将线程安放在线程池里,该方法的原型如下:

  //将一个线程放进线程池,该线程的Start()方法将调用WaitCallback代理对象代表的函数
  public static bool QueueUserWorkItem(WaitCallback);
  //重载的方法如下,参数object将传递给WaitCallback所代表的方法
  public static bool QueueUserWorkItem(WaitCallback, object);

  要注意的是,ThreadPool类也是一个静态类,你不能也不必要生成它的对象,而且一旦使用该方法在线程池中添加了一个项目,那么该项目将是没有办法取消的。在这里你无需自己建立线程,只需把你要做的工作写成函数,然后作为参数传递给ThreadPool.QueueUserWorkItem()方法就行了,传递的方法就是依靠WaitCallback代理对象,而线程的建立、管理、运行等等工作都是由系统自动完成的,你无须考虑那些复杂的细节问题,线程池的优点也就在这里体现出来了,就好像你是公司老板——只需要安排工作,而不必亲自动手。

下面的例程演示了ThreadPool的用法。首先程序创建了一个ManualResetEvent对象,该对象就像一个信号灯,可以利用它的信号来通知其它线程,本例中当线程池中所有线程工作都完成以后,ManualResetEvent的对象将被设置为有信号,从而通知主线程继续运行。它有几个重要的方法:Reset(),Set(),WaitOne()。初始化该对象时,用户可以指定其默认的状态(有信号/无信号),在初始化以后,该对象将保持原来的状态不变直到它的Reset()或者Set()方法被调用,Reset()方法将其设置为无信号状态,Set()方法将其设置为有信号状态。WaitOne()方法使当前线程挂起直到ManualResetEvent对象处于有信号状态,此时该线程将被激活。然后,程序将向线程池中添加工作项,这些以函数形式提供的工作项被系统用来初始化自动建立的线程。当所有的线程都运行完了以后,ManualResetEvent.Set()方法被调用,因为调用了ManualResetEvent.WaitOne()方法而处在等待状态的主线程将接收到这个信号,于是它接着往下执行,完成后边的工作。

  using System;
  using System.Collections;
  using System.Threading;

  //这是用来保存信息的数据结构,将作为参数被传递
  public class SomeState
  {
  public int Cookie;
  public SomeState(int iCookie)
  {
    Cookie = iCookie;
  }
  }

  public class Alpha
  {
  public Hashtable HashCount;
  public ManualResetEvent eventX;
  public static int iCount = 0;
  public static int iMaxCount = 0;
  public Alpha(int MaxCount)
  {
    HashCount = new Hashtable(MaxCount);
    iMaxCount = MaxCount;
  }

  file://线程池里的线程将调用Beta()方法
  public void Beta(Object state)
  {
    //输出当前线程的hash编码值和Cookie的值
    Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),
    ((SomeState)state).Cookie);
    Console.WriteLine("HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}", HashCount.Count, Thread.CurrentThread.GetHashCode());
    lock (HashCount)
    {
    file://如果当前的Hash表中没有当前线程的Hash值,则添加之
    if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))
      HashCount.Add (Thread.CurrentThread.GetHashCode(), 0);
    HashCount[Thread.CurrentThread.GetHashCode()] =
((int)HashCount[Thread.CurrentThread.GetHashCode()])+1;
    }

    int iX = 2000;
    Thread.Sleep(iX);
    //Interlocked.Increment()操作是一个原子操作,具体请看下面说明
    Interlocked.Increment(ref iCount);
    if (iCount == iMaxCount)
    {
    Console.WriteLine();
    Console.WriteLine("Setting eventX ");
    eventX.Set();
    }
  }
  }

  public class SimplePool
  {
  public static int Main(string[] args)
  {
    Console.WriteLine("Thread Pool Sample:");
    bool W2K = false;
    int MaxCount = 10;//允许线程池中运行最多10个线程
    //新建ManualResetEvent对象并且初始化为无信号状态
    ManualResetEvent eventX = new ManualResetEvent(false);
    Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount);
    Alpha oAlpha = new Alpha(MaxCount); file://创建工作项
    //注意初始化oAlpha对象的eventX属性
    oAlpha.eventX = eventX;
    Console.WriteLine("Queue to Thread Pool 0");
    try
    {
    file://将工作项装入线程池
    file://这里要用到Windows 2000以上版本才有的API,所以可能出现NotSupportException异常
    ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),
    new SomeState(0));
    W2K = true;
    }
    catch (NotSupportedException)
    {
    Console.WriteLine("These API's may fail when called on a non-Windows 2000 system.");
    W2K = false;
    }
    if (W2K)//如果当前系统支持ThreadPool的方法.
    {
    for (int iItem=1;iItem < MaxCount;iItem++)
    {
      //插入队列元素
      Console.WriteLine("Queue to Thread Pool {0}", iItem);
      ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),new SomeState(iItem));
    }
    Console.WriteLine("Waiting for Thread Pool to drain");
    file://等待事件的完成,即线程调用ManualResetEvent.Set()方法
    eventX.WaitOne(Timeout.Infinite,true);
    file://WaitOne()方法使调用它的线程等待直到eventX.Set()方法被调用
    Console.WriteLine("Thread Pool has been drained (Event fired)");
    Console.WriteLine();
    Console.WriteLine("Load across threads");
    foreach(object o in oAlpha.HashCount.Keys)
    Console.WriteLine("{0} {1}", o, oAlpha.HashCount[o]);
    }
    Console.ReadLine();
    return 0;

  }
  }

  程序中有些小地方应该引起我们的注意。SomeState类是一个保存信息的数据结构,在上面的程序中,它作为参数被传递给每一个线程,你很容易就能理解这个,因为你需要把一些有用的信息封装起来提供给线程,而这种方式是非常有效的。程序出现的InterLocked类也是专为多线程程序而存在的,它提供了一些有用的原子操作,所谓原子操作就是在多线程程序中,如果这个线程调用这个操作修改一个变量,那么其他线程就不能修改这个变量了,这跟lock关键字在本质上是一样的。

  我们应该彻底地分析上面的程序,把握住线程池的本质,理解它存在的意义是什么,这样我们才能得心应手地使用它。下面是该程序的输出结果:

  Thread Pool Sample:
  Queuing 10 items to Thread Pool
  Queue to Thread Pool 0
  Queue to Thread Pool 1
  ...
  ...
  Queue to Thread Pool 9
  Waiting for Thread Pool to drain
  98 0 :
  HashCount.Count==0, Thread.CurrentThread.GetHashCode()==98
  100 1 :
  HashCount.Count==1, Thread.CurrentThread.GetHashCode()==100
  98 2 :
  ...
  ...
  Setting eventX
  Thread Pool has been drained (Event fired)
  Load across threads
  101 2
  100 3
  98 4
  102 1

  与ThreadPool类不同,Timer类的作用是设置一个定时器,定时执行用户指定的函数,而这个函数的传递是靠另外一个代理对象TimerCallback,它必须在创建Timer对象时就指定,并且不能更改。定时器启动后,系统将自动建立一个新的线程,并且在这个线程里执行用户指定的函数。下面的语句初始化了一个Timer对象:

  Timer timer = new Timer(timerDelegate, s,1000, 1000);

  第一个参数指定了TimerCallback代理对象;第二个参数的意义跟上面提到的WaitCallback代理对象的一样,作为一个传递数据的对象传递给要调用的方法;第三个参数是延迟时间——计时开始的时刻距现在的时间,单位是毫秒;第四个参数是定时器的时间间隔——计时开始以后,每隔这么长的一段时间,TimerCallback所代表的方法将被调用一次,单位也是毫秒。这句话的意思就是将定时器的延迟时间和时间间隔都设为1秒钟。

  定时器的设置是可以改变的,只要调用Timer.Change()方法,这是一个参数类型重载的方法,一般使用的原型如下:

   public bool Change(long, long);

  下面这段代码将前边设置的定时器修改了一下:

   timer.Change(10000,2000);

  很显然,定时器timer的时间间隔被重新设置为2秒,停止计时10秒后生效。

  下面这段程序演示了Timer类的用法。

  using System;
  using System.Threading;
  class TimerExampleState
  {
  public int counter = 0;
  public Timer tmr;
  }

  class App
  {
  public static void Main()
  {
    TimerExampleState s = new TimerExampleState();

    //创建代理对象TimerCallback,该代理将被定时调用
    TimerCallback timerDelegate = new TimerCallback(CheckStatus);

    //创建一个时间间隔为1s的定时器
    Timer timer = new Timer(timerDelegate, s,1000, 1000);
    s.tmr = timer;

    //主线程停下来等待Timer对象的终止
    while(s.tmr != null)
    Thread.Sleep(0);
    Console.WriteLine("Timer example done.");
    Console.ReadLine();
  }
  file://下面是被定时调用的方法

  static void CheckStatus(Object state)
  {
    TimerExampleState s =(TimerExampleState)state;
    s.counter++;
    Console.WriteLine("{0} Checking Status {1}.",DateTime.Now.TimeOfDay, s.counter);
    if(s.counter == 5)
    {
    file://使用Change方法改变了时间间隔
    (s.tmr).Change(10000,2000);
    Console.WriteLine("changed...");
    }
    if(s.counter == 10)
    {
    Console.WriteLine("disposing of timer...");
    s.tmr.Dispose();
    s.tmr = null;
    }
  }
  }

  程序首先创建了一个定时器,它将在创建1秒之后开始每隔1秒调用一次CheckStatus()方法,当调用5次以后,在CheckStatus()方法中修改了时间间隔为2秒,并且指定在10秒后重新开始。当计数达到10次,调用Timer.Dispose()方法删除了timer对象,主线程于是跳出循环,终止程序。程序执行的结果如下:

  上面就是对ThreadPool和Timer两个类的简单介绍,充分利用系统提供的功能,可以为我们省去很多时间和精力——特别是对很容易出错的多线程程序。同时我们也可以看到.net Framework强大的内置对象,这些将对我们的编程带来莫大的方便。

转载于:https://www.cnblogs.com/sql4me/archive/2009/04/29/1446091.html

线程池和定时器——多线程的自动管理(转载)相关推荐

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

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

  2. C#多线程学习(五) 多线程的自动管理(定时器) (转载系列)——继续搜索引擎研究...

    Timer类:设置一个定时器,定时执行用户指定的函数.               定时器启动后,系统将自动建立一个新的线程,执行用户指定的函数. 初始化一个Timer对象: Timer timer ...

  3. C#多线程学习之(五)使用定时器进行多线程的自动管理

    本文实例讲述了C#多线程学习之使用定时器进行多线程的自动管理.分享给大家供大家参考.具体分析如下: Timer类:设置一个定时器,定时执行用户指定的函数. 定时器启动后,系统将自动建立一个新的线程,执 ...

  4. 并发库应用之三 线程池与定时器应用

    在TCP服务器编程模型的原理,每一个客户端连接用一个单独的线程为之服务,当与客户端的会话结束时,线程也就结束了,即每来一个客户端连接,服务器端就要创建一个新线程.如果访问服务器的客户端很多,那么服务器 ...

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

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

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

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

  7. 24.多线程(等待唤醒机制,volatile,CAS 算法,线程池,定时器,设计模式)

    1.线程间的等待唤醒机制 Object 类中   void wait ()  在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待.         ...

  8. 多线程 4——线程通信、线程池、定时器

    多线程 一.线程通信 1.等待集 2.wait()方法 3.notify() / notifyAll()方法 4.等待队列/同步队列 5.生产者消费者模型 二.线程池 1.jdk中的线程池 2.自己实 ...

  9. java定时线程池_java 定时器线程池(ScheduledThreadPoolExecutor)的实现

    前言 定时器线程池提供了定时执行任务的能力,即可以延迟执行,可以周期性执行.但定时器线程池也还是线程池,最底层实现还是ThreadPoolExecutor,可以参考我的另外一篇文章多线程–精通Thre ...

最新文章

  1. 【Scala-spark.mlib】通过Maven工程导入Mlib库
  2. 20亿参数+30亿张图像,刷新ImageNet最高分!谷歌大脑华人研究员领衔发布最强Transformer...
  3. 您应该知道Python 3.10中的新特性!
  4. (解题报告)L1-032 Left-pad (20分)——15行代码AC
  5. 嵌套字典|python_Python | 如果不是,则使用嵌套,根据销售额计算折扣
  6. Python的小整数对象池
  7. 「代码随想录」本周学习小结!(动态规划系列三)
  8. 程序员如何转型项目经理?
  9. maya为什么不能导出fbx_maya从 Maya 导出为 FBX 文件,MAYA
  10. 贝叶斯派的概率图模型概述(总)
  11. 密码库LibTomCrypt学习记录——(2.13)分组密码算法的工作模式——CCM加密认证模式
  12. 转载:“凤求凰”的解释,有才
  13. boost noncopyable实现与ADL
  14. 华为交换机配置dhcp详细配置
  15. identifier of an instance of
  16. 三星证实遭黑客入侵:Galaxy手机源代码泄露
  17. Qt中Qlabel 图片拖放显示
  18. 教学计划编制问题(C语言)
  19. python解包(Unpacking)
  20. 《Electron入门与实战》创作路上的那些事儿

热门文章

  1. mysql-5.7.24-linux_Linux下安装mysql-5.7.24
  2. 打开终端输入没反应_忘掉Iterm2,试试这款跨平台终端工具
  3. Spring Security OAuth2 授权失败(401)
  4. mysql sql汇总查询将两个结果集合并一行展示
  5. Java使用RabbitMQ之订阅分发(Topic)
  6. Git提交时提示“Please make sure you have the correct access rights and the repository exists.”的解决方法
  7. Android开发笔记(一百四十九)约束布局ConstraintLayout
  8. Android开发笔记(一百一十四)发布工具
  9. Go语言标准库之fmt.Print
  10. Cosmos互联链通信技术规范(上)