多线程可以提高应用程序的效率,这是肯定的,但是,效率是不是最优的呢,是不是觉得多线程很复杂呢?

前面学习线程的知道,用多线程需要CreateThread创建线程,还要关闭线程。另外,多线程有时候还要对资源进行同步,也就是说,需要用到事件,信标,互斥对象。

当然,线程与进程比较,无论速度,对资源的访问,安全性上面线程都有非常大的优势。但是,创建与销毁线程并不是免费的。

要创建一个线程,需要分配和初始化一个内核对象,也需要分配和初始化线程的堆栈空间,而且 Windows® 为进程中的每个 DLL 发送一个 DLL_THREAD_ATTACH 通知,使磁盘中的页分配到内存中,从而执行代码。当线程终止时,给每个 DLL 都发送一个 DLL_THREAD_DETACH 通知,线程的堆栈空间被释放,内核对象亦被释放(如果其使用数达到 0)。因此,与创建和销毁线程相关的许多开销都和创建线程原本要执行的工作无关。

为了更优化效率,Windows提供了线程池的概念。

线程池使创建,管理与撤销线程变得更容易。

我觉得,线程池可以:
    *初始化线程,动态得创建线程。
    *预分配线程池的内存空间
    *优先级的排队
    *管理线程,撤销线程。

Jeffer Richter在"CLR线程池"文章中讲到CLR中线程的特性:
    当 CLR 初始化时,其线程池中不含有线程。当应用程序要创建线程来执行任务时,该应用程序应请求线程池线程来执行任务。线程池知道后将创建一个初始线程。该新线程经历的初始化和其他线程一样;但是任务完成后,该线程不会自行销毁。相反,它会以挂起状态返回线程池。如果应用程序再次向线程池发出请求,那么这个挂起的线程将激活并执行任务,而不会创建新线程。这节约了很多开销。只要线程池中应用程序任务的排队速度低于一个线程处理每项任务的速度,那么就可以反复重用同一线程,从而在应用程序生存期内节约大量开销。

那么,如果线程池中应用程序任务排队的速度超过一个线程处理任务的速度,则线程池将创建额外的线程。当然,创建新线程确实会产生额外开销,但应用程序在其生存期中很可能只请求几个线程来处理交给它的所有任务。因此,总体来说,通过使用线程池可以提高应用程序的性能。

现在您可能想知道,如果线程池包含许多线程而应用程序的工作负荷又在减少,将会发生什么事情。这种情况下,线程池包含几个长期挂起的线程,浪费着操作系统的资源。Microsoft 也考虑到了这个问题。当线程池线程自身挂起时,它等待 40 秒钟。如果 40 秒过去后线程无事可做,则该线程将激活并自行销毁,释放出它使用的全部操作系统资源(堆栈、内核对象,等等)。同时,激活并自行销毁线程可能并不影响应用程序的性能,因为应用程序做的事情毕竟不是太多,否则就会恢复执行该线程。顺便说一句,尽管我说线程池中的线程是在 40 秒内自行激活的,但实际上这个时间并没有验证并可以改变。

线程池的一个绝妙特性是:它是启发式的。如果您的应用程序需要执行很多任务,那么线程池将创建更多的线程。如果您的应用程序的工作负载逐渐减少,那么线程池线程将自行终止。线程池的算法确保它仅包含置于其上的工作负荷所需要的线程数!

线程池可以提供的功能:
    1.异步调用功能:一般我们调用函数都是同步的,即函数返回后,才执行下一句代码。但是,用线程池可以异步的调用我们的函数,调用后,马上执行下一句,至于线程的什么时候返回,是主线程所不知道的。
    另外,请注意,永远不要调用任何可以自己创建线程的方法;如果需要,CLR 的线程池将自动创建线程,如果可能还将重用现有的线程。另外,线程处理回调方法后不会立即销毁该线程;它将返回到线程池并准备处理队列中的其他工作项。使用System.Theading.TheadPool中的QueueUserWorkItem 会使您的应用程序更有效,因为您将不需要为每个客户端请求创建和销毁线程。
        例:下面写了一个线程池来实现异步调用的方法:

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

public class MyClass
{
    public static void Main()
    {
        Console.WriteLine("Main Thread:Queuing an aynchronous operation");
        ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(MyAsyncOperation));
        
        Console.WriteLine("Main Thread:Performing other operation");
        Console.WriteLine("Main thread: Pausing to simulate doing other operations.");
        Console.ReadLine();
    }
    
    static void MyAsyncOperation(object state)
    {
        Console.WriteLine("ThreadPool thread:Perform aynchronous operation");
        Thread.Sleep(5000);
    }
}

知道它的结果是什么吗?如果是传统的调用MyAsyncOperation函数,可以肯定得出结论,是输出:
    Main Thread:Queuing an aynchronous operation
    ThreadPool thread:Perform aynchronous operat
    Main Thread:Performing other operation
    Main thread: Pausing to simulate doing other operations
    但是,用了线程池,创建一个线程来执行这个函数,结果却大不一样。以下是执行结果的截图:
   

2.以一定的时间间隔调用方法:
    如果应用程序需要在某个时间执行某项任务,或者定时执行某个任务。那么,就使用线程池吧。
    System.Threading.Timer类可以为你构造这样的功能。函数原型如下:

public Timer(TimerCallback callback, Object state,   Int32 dueTime, Int32 period);
   public Timer(TimerCallback callback, Object state,   UInt32 dueTime, UInt32 period);
   public Timer(TimerCallback callback, Object state,   Int64 dueTime, Int64 period);
   public Timer(TimerCallback callback, Object state,   Timespan dueTime, TimeSpan period); 

用户定义的线程函数可以这样定义:

public delegate void TimerCallback(Object state);

我们写一下让线程池线程立即调用一个方法,并且每隔 2000 毫秒(或两秒)再次调用的应用程序。
    以下是程序:

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

public class MyClass
{
    static int Times=0;
    public static void Main()
    {
        Console.WriteLine("Checking for status updates every 2 seconds.");
        Console.WriteLine("Hit Enter to terminate the sample");
        Timer timer    =    new Timer(new TimerCallback(CheckStatus),"Timeing",0,2000);
        
        Console.ReadLine();
    }
    
    static void CheckStatus(object state)
    {
        Console.WriteLine("Checking Status:"+Convert.ToString(state)+" "+(Times++).ToString()+"'s");
    }
}

以下是输出的截图:
   

3.当单个内核对象得到信号通知时调用方法
    Jeffer Richter文章中说到:

Microsoft 研究人员在做性能研究时发现,许多应用程序生成线程,只是为了等待某单个内核对象得到信号通知。一旦该对象得到信号通知,这个线程就将某种通知发送给另一个线程,然后环回,等待该对象再次发出信号。有些开发人员编写的代码中甚至有几个线程,而每个线程都在等待一个对象。这是系统资源的巨大浪费。因此,如果当前您的应用程序中有多个线程在等待单个内核对象得到信号通知,那么线程池仍将是您提高应用程序性能的最佳资源。
    
    至于它应该怎么使用呢?
    要让线程池线程在内核对象得到信号通知时调用您的回调方法,您可以再次利用 System.Threading.ThreadPool 类中定义的一些静态方法。要让线程池线程在内核对象得到信号通知时调用方法,您的代码必须调用一个重载的 RegisterWaitHandle 方法
      它的原型如下:

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state, 
   UInt32 milliseconds, Boolean executeOnlyOnce);

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state, 
   Int32 milliseconds, Boolean executeOnlyOnce);

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state, 
   TimeSpan milliseconds, Boolean executeOnlyOnce);

public static RegisterWaitHandle RegisterWaitForSingleObject(
   WaitHandle h, WaitOrTimerCallback callback, Object state,
   Int64 milliseconds, Boolean executeOnlyOnce); 

第一个参数h表示你要等待的内核对象,第二个参数callback表示调用的用户线程函数,第三参数state是传递给用户线程函数的参数,第四个参数milliseconds表示线程池内核对象得到信号通知前应该等待的时间,通常传递-1(就跟前面提到的函数WaitForSingleObject第二个参数作用相同),表示无限超时。第五个参数executeOnlyOnce 为真,那么线程池线程将仅执行回调方法一次。但是,如果 executeOnlyOnce 为假,那么线程池线程将在内核对象每次得到信号通知时执行回调方法。

客户端定义的函数原型:

public delegate void WaitOrTimerCallback(Object state,Boolean timedOut);

当调用回调方法时,会传递给它状态数据和 Boolean 值 timedOut。如果 timedOut 为假,则该方法知道它被调用的原因是内核对象得到信号通知。如果 timedOut 为真,则该方法知道它被调用的原因是内核对象在指定时间内没有得到信号通知。回调方法应该执行所有必需的操作。

看具体的代码:

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

public class MyClass
{
    public static void Main()
    {
        AutoResetEvent are = new AutoResetEvent(false); //自动事件对象
        RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(
             are, new WaitOrTimerCallback(EventSignalled), null, -1, false);
          for (Int32 x = 0 ; x < 5; x++) 
        {
         Thread.Sleep(5000);
         are.Set();
          }

          rwh.Unregister(null);
          Console.WriteLine("Hit Enter to terminate the sample");
          Console.ReadLine();

    }
    
    static void EventSignalled(object state,Boolean timedOut)
    {
        if (timedOut) 
             Console.WriteLine("Timed-out while waiting for the AutoResetEvent.");
           else 
             Console.WriteLine("The AutoResetEvent became signalled.");
    }
}

执行如图所示:
   

Jeff的原文:http://blog.chinaunix.net/article.php?articleId=43400&blogId=5958

转载于:https://www.cnblogs.com/shipfi/archive/2005/09/04/229899.html

线程池 And 线程池的使用(基于.net平台)相关推荐

  1. ReentrantLock+线程池+同步+线程锁

    1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...

  2. python线程池模块_python并发编程之进程池,线程池,协程

    需要注意一下 不能无限的开进程,不能无限的开线程 最常用的就是开进程池,开线程池.其中回调函数非常重要 回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去 ...

  3. 别再纠结线程池大小/线程数量了,没有固定公式的

    可能很多人都看到过一个线程数设置的理论: CPU 密集型的程序 - 核心数 + 1 I/O 密集型的程序 - 核心数 * 2 不会吧,不会吧,真的有人按照这个理论规划线程数? 线程数和CPU利用率的小 ...

  4. 【Java 并发编程】线程池机制 ( 线程池阻塞队列 | 线程池拒绝策略 | 使用 ThreadPoolExecutor 自定义线程池参数 )

    文章目录 一.线程池阻塞队列 二.拒绝策略 三.使用 ThreadPoolExecutor 自定义线程池参数 一.线程池阻塞队列 线程池阻塞队列是线程池创建的第 555 个参数 : BlockingQ ...

  5. 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )

    文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...

  6. 由浅入深理解Java线程池及线程池的如何使用

    前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...

  7. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  8. 并发编程——进程池与线程池

    一.简单介绍 在学习了多进程或多线程之后,我们可能会迫不及待地基于多进程或多线程做一些开发,然而毫无节制的开启进程或线程是十分危险的. 服务开启的进程数或线程数都会随着并发的客户端数目地增多而增多,这 ...

  9. python 线程池_Python线程池及其原理和使用(超级详细)

    系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互.在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 线程池在系统启动时即 ...

最新文章

  1. 使用 pm2-web 监控 pm2 服务运行状态
  2. edmonds算法matlab,匈牙利算法的matlab实现
  3. php评论获取时间,WordPress函数comment_date获取评论发布时间
  4. python switch语句_几个Python里的骚操作
  5. 解决使用adprep升级windows2003/win2008/win2012域时遇到的问题
  6. @程序员,你真得了解每天打交道的字节吗?
  7. recyclervie刷新到底部_自定义RecyclerView添加HeaderView,添加FooterView,实现滑动到底部,加载更多...
  8. Web前端开发神器-WebStorm
  9. vs2017结合qt开发,vs报错找不到库(解决方案)
  10. 【EPS精品教程】EPS2016三维测图版安装教程(附EPS2016安装包下载地址)
  11. 常见的三个网络协议的区别:TCP/IP、NetBEUI、IPX/SPX
  12. 等保2.0中的工业控制系统(ICS)指的是什么
  13. P2504 [HAOI2006]聪明的猴子
  14. 【精简】海姆利克急救法+心肺复苏 基础急救技能
  15. guitar chord html5,‎App Store 上的“吉他和弦(基本): GUITAR CHORD”
  16. Python爬取10529条《三十而已》热评,看看大家都说了些啥
  17. 为什么亿级数据量时要使用位图?位图和布隆过滤器有什么关系?
  18. 仿苹果手机闹钟_高仿iOS系统闹钟 UserNotifications
  19. 文字转图片,文字水印图片,合成图片,教你 Python 生成网站原创配图!
  20. 质量管理之代码的圈复杂度

热门文章

  1. python【蓝桥杯vip练习题库】ALGO-232找零钱(贪心 模拟)
  2. python【Matlibplot绘图库】绘制用于学术论文投稿的黑白图片
  3. 解决Tensorflow 使用时cpu编译不支持警告
  4. UVa 10375 Choose and divide
  5. sql优化ppt_Spark优化 | Spark 3.0 中七个必须知道的 SQL 性能优化
  6. sysbench mysql测试_使用sysbench对MySQL进行测试
  7. 怎样做网络推广浅析网站被K之后,优化人员们要注意的方面是哪些?
  8. 网络营销推广软件浅析网络优化时该如何更好的判断友链的质量?
  9. 网站改版都要注重哪些因素?
  10. 网站推广的三大基本方式