zoukankan      html  css  js  c++  java
  • C# 线程池

          许多应用程序使用多个线程,但这些线程经常在休眠状态中耗费大量的时间来等待事件发生。其他线程可能进入休眠状态,并且仅定期被唤醒以轮询更改或更新状态信息,然后再次进入休眠状态。为了简化对这些线程的管理,.NET框架为每一个进程提供了一个线程池,使应用程序能够根据需要来有效地利用多个线程。一个线程监视排到线程池的若干个等待操作的状态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。线程池中的线程由系统进行管理,程序员不需要费力于线程管理,可以集中精力处理应用程序任务。

         线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间之后创建另一个辅助线程。但线程的数目永远不会超过最大值。超过最大值的其他线程可以排队,但它们要等到其他线程完成后才启动。

         线程池特别适合于执行一些需要多个线程的任务。使用线程池能够优化这些任务的执行过程,从而提高吞吐量,它不仅能够使系统针对此进程优化该执行过程,而且还能够使系统针对计算机上的其他进程优化该执行过程。如果需要启动多个不同的任务,而不想分别设置每个线程的属性,则可以使用线程池。如果应用程序需要对线程进行特定的控制,则不适合使用线程池,需要创建并管理自己的线程。不适合使用线程池的情形包括:

    — 如果需要使一个任务具有特定的优先级。

    — 如果具有可能会长时间运行(并因此阻塞其他任务)的任务。

    — 如果需要将线程放置到单线程单元中(线程池中的线程均处于多线程单元中)。

    — 如果需要用永久标识来标识和控制线程,比如想使用专用线程来中止该线程,将其挂起或按名称发现它。System.Threading.ThreadPool类实现了线程池。ThreadPool类是一个静态类,它提供了管理线程池的一系列方法。ThreadPool.QueueUserWorkItem方法在线程池中创建一个线程池线程来执行指定的方法(用委托WaitCallback来表示),并将该线程排入线程池的队列等待执行。QueueUserWorkItem方法的原型为:

    public static Boolean QueueUserWorkItem(WaitCallback wc, Object state);

    public static Boolean QueueUserWorkItem(WaitCallback wc);

        这些方法将“工作项”(和可选状态数据)排列到线程池的线程中,并立即返回。工作项只是一种方法(由wc参数标识),它被调用并传递给单个参数,即状态(状态数据)。没有状态参数的QueueUserWorkItem版本将null传递给回调方法。线程池中的某些线程将调用System.Threading.WaitCallback委托表示的回调方法来处理该工作项。回调方法必须与System.Threading.WaitCallback委托类型相匹配。WaitCallback定义如下:

    public delegate void WaitCallback(Object state);

    调用QueueUserWorkItem时传入的Object类型参数将传递到任务过程,可以通过这种方式来向任务过程传递参数。如果任务过程需要多个参数,可以定义包含这些数据的类,并将类的实例强制转换为Object数据类型。每个进程都有且只有一个线程池。当进程启动时,线程池并不会自动创建。当第一次将回调方法排入队列(比如调用ThreadPool.QueueUserWorkItem方法)时才会创建线程池。一个线程监视所有已排队到线程池中的任务。当某项任务完成后,线程池中的线程将执行相应的回调方法。在对一个工作项进行排队之后将无法取消它。线程池中的线程数目仅受可用内存的限制。但是,线程池将对允许在进程中同时处于活动状态的线程数目强制实施限制(这取决于CPU的数目和其他因素)。默认情况下,每个系统处理器最多可以运行25个线程池线程。通过使用ThreadPool.GetMaxThreads和ThreadPool.SetMax Threads方法,可以获取和设置线程池的最大线程数。即使是在所有线程都处于空闲状态时,线程池也会维持最小的可用线程数,以便队列任务可以立即启动。将终止超过此最小数目的空闲线程,以节省系统资源。默认情况下,每个处理器维持一个空闲线程。使用ThreadPool.GetMinThreads和ThreadPool.SetMinThreads方法可以获取和设置线程池所维持的空闲线程数。

    下面的程序用于计算如下的函数的值:

    // ThreadPool.cs
    
     
    
    // 线程池示例
    
     
    
    using System;
    
     
    
    using System.Threading;
    
     
    
    public class Test
    
     
    
    {
    
     
    
        // 存放要计算的数值的字段
    
     
    
        static double number1 = -1;
    
     
    
        static double number2 = -1;
    
     
    
        public static void Main()
    
     
    
        {
    
     
    
            // 获取线程池的最大线程数和维护的最小空闲线程数
    
     
    
            int maxThreadNum, portThreadNum;
    
     
    
            int minThreadNum;
    
     
    
            ThreadPool.GetMaxThreads(out maxThreadNum, out portThreadNum);
    
     
    
            ThreadPool.GetMinThreads(out minThreadNum, out portThreadNum);
    
     
    
            Console.WriteLine("最大线程数:{0}", maxThreadNum);
    
     
    
            Console.WriteLine("最小空闲线程数:{0}", minThreadNum);
    
     
    
            // 函数变量值
    
     
    
            int x = 15600;
    
     
    
            // 启动第一个任务:计算x的8次方
    
     
    
            Console.WriteLine("启动第一个任务:计算{0}的8次方。", x);
    
     
    
            ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc1), x);
    
     
    
            // 启动第二个任务:计算x的8次方根
    
     
    
            Console.WriteLine("启动第二个任务:计算{0}的8次方根。", x);
    
     
    
            ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc2), x);
    
     
    
            // 等待,直到两个数值都完成计算
    
     
    
            while (number1 == -1 || number2 == -1) ;
    
     
    
            // 打印计算结果
    
     
    
            Console.WriteLine("y({0}) = {1}", x, number1 + number2);
    
     
    
        }
    
     
    
        // 启动第一个任务:计算x的8次方
    
     
    
        static void TaskProc1(object o)
    
     
    
        {
    
     
    
            number1 = Math.Pow(Convert.ToDouble(o), 8);
    
     
    
        }
    
     
    
        // 启动第二个任务:计算x的8次方根
    
     
    
        static void TaskProc2(object o)
    
     
    
        {
    
     
    
            number2 = Math.Pow(Convert.ToDouble(o), 1.0 / 8.0);
    
     
    
        }
    
     
    
    }

    该程序的输出结果为:

    最大线程数:25

    最小空闲线程数:1

    启动第一个任务:计算15600的8次方。

    启动第二个任务:计算15600的8次方根。

    y(15600) = 3.50749278894883E+33

         “线程池”是可以用来在后台执行多个任务的线程集合。(有关背景信息,请参见使用线程处理。)这使主线程可以自由地异步执行其他任务。线程池通常用于服务器应用程序。每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理。一旦池中的某个线程完成任务,它将返回到等待线程队列中,等待被再次使用。这种重用使应用程序可以避免为每个任务创建新线程的开销。线程池通常具有最大线程数限制。如果所有线程都繁忙,则额外的任务将放入队列中,直到有线程可用时才能够得到处理。您可以实现自己的线程池,但是通过 ThreadPool 类使用 .NET Framework 提供的线程池更容易一些。

         下面的示例使用 .NET Framework 线程池计算 20 和 40 之间的十个数的 Fibonacci 结果。每个 Fibonacci 结果都由 Fibonacci 类表示,该类提供一种名为ThreadPoolCallback 的方法来执行此计算。将创建表示每个 Fibonacci 值的对象,ThreadPoolCallback 方法将传递给 QueueUserWorkItem,它分配池中的一个可用线程来执行此方法。由于为每个 Fibonacci 对象都提供了一个半随机值来进行计算,而且十个线程都将竞争处理器时间,因此无法提前知道十个结果全部计算出来所需的时间。因此在构造期间为每个 Fibonacci 对象传递 ManualResetEvent 类的一个实例。当计算完成时,每个对象都通知提供的事件对象,使主线程用 WaitAll 阻止执行,直到十个 Fibonacci 对象全部计算出了结果。然后 Main 方法将显示每个 Fibonacci 结果。

    Code:

    using System;
    
    using System.Threading;
    
     
    
    public class Fibonacci
    
    {
    
        public Fibonacci(int n, ManualResetEvent doneEvent)
    
        {
    
            _n = n;
    
            _doneEvent = doneEvent;
    
        }
    
     
    
        // Wrapper method for use with thread pool.
    
        public void ThreadPoolCallback(Object threadContext)
    
        {
    
            int threadIndex = (int)threadContext;
    
            Console.WriteLine("thread {0} started...", threadIndex);
    
            _fibOfN = Calculate(_n);
    
            Console.WriteLine("thread {0} result calculated...", threadIndex);
    
            _doneEvent.Set();
    
        }
    
     
    
        // Recursive method that calculates the Nth Fibonacci number.
    
        public int Calculate(int n)
    
        {
    
            if (n <= 1)
    
            {
    
                return n;
    
            }
    
     
    
            return Calculate(n - 1) + Calculate(n - 2);
    
        }
    
     
    
        public int N { get { return _n; } }
    
        private int _n;
    
     
    
        public int FibOfN { get { return _fibOfN; } }
    
        private int _fibOfN;
    
     
    
        private ManualResetEvent _doneEvent;
    
    }
    
     
    
    public class ThreadPoolExample
    
    {
    
        static void Main()
    
        {
    
            const int FibonacciCalculations = 10;
    
     
    
            // One event is used for each Fibonacci object
    
            ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
    
            Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
    
            Random r = new Random();
    
     
    
            // Configure and launch threads using ThreadPool:
    
            Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
    
            for (int i = 0; i < FibonacciCalculations; i++)
    
            {
    
                doneEvents[i] = new ManualResetEvent(false);
    
                Fibonacci f = new Fibonacci(r.Next(20,40), doneEvents[i]);
    
                fibArray[i] = f;
    
                ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
    
            }
    
     
    
            // Wait for all threads in pool to calculation...
    
            WaitHandle.WaitAll(doneEvents);
    
            Console.WriteLine("All calculations are complete.");
    
     
    
            // Display the results...
    
            for (int i= 0; i<FibonacciCalculations; i++)
    
            {
    
                Fibonacci f = fibArray[i];
    
                Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
    
            }
    
        }
    
    }
  • 相关阅读:
    MySQL执行计划extra中的using index 和 using where using index 的区别
    Python + Apache Kylin 让数据分析更加简单!
    性能测试解读:Kyligence vs Spark SQL
    greenplum 表在各个节点数据的分布情况
    postgresql drop表后空间不释放
    PostgreSQL 查看表、索引等创建时间
    postgresql Kill掉正在执行的SQL语句
    linux ps命令查看最消耗CPU、内存的进程
    Linux shell
    TPC-H 下载参考
  • 原文地址:https://www.cnblogs.com/luohengstudy/p/3156625.html
Copyright © 2011-2022 走看看