转:专题二线程池中的工作者线程

目录:

一、上节补充

二、CLR线程池基础

三、通过线程池的工作者线程实现异步

四、使用委托实现异步

五、任务 

一、上节补充

对于Thread类还有几个常用方法需要说明的。 

1.1 Suspend和Resume方法 

这两个方法在.net Framework 1.0的时候就支持的方法,他们分别可以挂起线程和恢复挂起的线程。但在.net Framework 2.0以后的版本中这两个方法都过时了,MSDN的解释是这样: 

警告: 

不要使用 Suspend 和 Resume 方法来同步线程的活动。您无法知道挂起线程时它正在执行什么代码。如果您在安全权限评估期间挂起持有锁的线程,则 AppDomain中的其他线程可能被阻止。如果您在线程正在执行类构造函数时挂起它,则 AppDomain中尝试使用该类的其他线程将被阻止。这样很容易发生死锁。 

对于这个解释可能有点抽象吧,让我们来看看一段代码可能会清晰点:

using System.Threading;
using System;
class Program
{
    static void Main(string[] args)
    {
        // 创建一个线程来测试
        Thread thread1 = new Thread(TestMethod);
        thread1.Name = "Thread1";
        thread1.Start();
        Thread.Sleep(2000);
        Console.WriteLine("主线程正在运行中...");
        //int b = 0;
        //int a = 3 / b;
        //Console.WriteLine(a);
        thread1.Resume();
        Console.Read();
    }

    private static void TestMethod()
    {
        Console.WriteLine("Thread: {0} 已经挂起!", Thread.CurrentThread.Name);
        //将当前线程挂起
        Thread.CurrentThread.Suspend();
        Console.WriteLine("Thread: {0} 已经恢复!", Thread.CurrentThread.Name);
    }
}

在上面这段代码中thread1线程是在主线程中恢复的,但当主线程发生异常时,这时候就thread1一直处于挂起状态,此时thread1所使用的资源就不能释放(除非强制终止进程),当另外线程需要使用这快资源的时候, 这时候就很可能发生死锁现象。

上面一段代码还存在一个隐患,请看下面一小段代码: 

using System.Threading;
using System;
class Program
{
    static void Main(string[] args)
    {
        // 创建一个线程来测试
        Thread thread1 = new Thread(TestMethod);
        thread1.Name = "Thread1";
        thread1.Start();
        Console.WriteLine("Main线程正在运行中...");
        thread1.Resume();
        Console.Read();
    }

    private static void TestMethod()
    {
        Console.WriteLine("Thread: {0} 已经挂起!", Thread.CurrentThread.Name);
        Thread.Sleep(1000);

        //将当前线程挂起
        Thread.CurrentThread.Suspend();
        Console.WriteLine("Thread: {0} 已经恢复!", Thread.CurrentThread.Name);
    }
}

 当主线程跑(运行)的太快,做完自己的事情去唤醒thread1时,此时thread1还没有挂起而起唤醒thread1,此时就会出现异常了。并且上面使用的Suspend和Resume方法,编译器已经出现警告了,提示这两个方法已经过时, 所以在我们平时使用中应该尽量避免。

1.2 Abort和 Interrupt方法

Abort方法和Interrupt都是用来终止线程的,但是两者还是有区别的。

1、他们抛出的异常不一样,Abort 方法抛出的异常是ThreadAbortException, Interrupt抛出的异常为ThreadInterruptedException

2、调用interrupt方法的线程之后可以被唤醒,然而调用Abort方法的线程就直接被终止不能被唤醒的。

下面一段代码是演示Abort方法的使用 

using System;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread abortThread = new Thread(AbortMethod);
            abortThread.Name = "Abort Thread";
            abortThread.Start();
            Thread.Sleep(1000);
            try
            {
                abortThread.Abort();
            }
            catch
            {
                Console.WriteLine("{0} 异常发生在主线程中", Thread.CurrentThread.Name);
                Console.WriteLine("{0} 状态是:{1} 在主线程中 ", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
            finally
            {
                Console.WriteLine("{0} 状态是:{1} 在主线程中 ", abortThread.Name, abortThread.ThreadState);
            }

            abortThread.Join();
            Console.WriteLine("{0} 状态是:{1} ", abortThread.Name, abortThread.ThreadState);
            Console.Read();

        }

        private static void AbortMethod()
        {
            try
            {
                Thread.Sleep(5000);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
                Console.WriteLine("{0} 异常发生在 Abort Thread中", Thread.CurrentThread.Name);
                Console.WriteLine("{0} 状态是:{1} 在Abort Thread中 ", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
            finally
            {
                Console.WriteLine("{0} 状态是:{1} 在Abort Thread中", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
        }
    }
}

 运行结果:

从运行结果可以看出,调用Abort方法的线程引发的异常类型为ThreadAbortException, 以及异常只会在 调用Abort方法的线程中发生,而不会在主线程中抛出,并且调用Abort方法后线程的状态不是立即改变为Aborted状态,而是从AbortRequested->Aborted。

Interrupt方法: 

using System;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread interruptThread = new Thread(AbortMethod);
            interruptThread.Name = "Interrupt Thread";
            interruptThread.Start();
            interruptThread.Interrupt();

            interruptThread.Join();
            Console.WriteLine("{0} 状态是:{1} ", interruptThread.Name, interruptThread.ThreadState);
            Console.Read();
        }

        private static void AbortMethod()
        {
            try
            {
                Thread.Sleep(5000);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
                Console.WriteLine("{0} 异常发生在Interrupt Thread中", Thread.CurrentThread.Name);
                Console.WriteLine("{0} 状态是:{1} 在 Interrupt Thread ", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
            finally
            {
                Console.WriteLine("{0} 状态是:{1} 在 Interrupt Thread", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
        }
    }
}

 运行结果:

从结果中可以得到,调用Interrupt方法抛出的异常为:ThreadInterruptException, 以及当调用Interrupt方法后线程的状态应该是中断的, 但是从运行结果看此时的线程因为了Join,Sleep方法而唤醒了线程,为了进一步解释调用Interrupt方法的线程可以被唤醒, 我们可以在线程执行的方法中运用循环,如果线程可以唤醒,则输出结果中就一定会有循环的部分,然而调用Abort方法线程就直接终止,就不会有循环的部分,下面代码相信大家看后肯定会更加理解两个方法的区别的:

using System;
using System.Threading;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(TestMethod);
            thread1.Start();
            Thread.Sleep(100);

            thread1.Interrupt();
            Thread.Sleep(3000);
            Console.WriteLine("在finnally代码块之后, 线程Thread1的状态是:{0}", thread1.ThreadState);
            Console.Read();
        }
        private static void TestMethod()
        {
            for (int i = 0; i < 4; i++)
            {
                try
                {
                    Thread.Sleep(2000);
                    Console.WriteLine("线程正在运行中...");
                }
                catch (Exception e)
                {
                    if (e != null)
                    {
                        Console.WriteLine("异常 {0} 抛出 ", e.GetType().Name);
                    }
                }
                finally
                {
                    Console.WriteLine("当前线程状态是:{0} ", Thread.CurrentThread.ThreadState);
                }
            }
        }
    }
}

结果为:

如果把上面的 thread1.Interrupt();改为 thread1.Abort(); 运行结果为:

1、Interrupt()和Abort():这两个关键字都是用来强制终止线程,不过两者还是有区别的

2、Interrupt():抛出的是 ThreadInterruptedException 异常; Abort():抛出的是  ThreadAbortException 异常。

3、Interrupt():如果终止工作线程,只能管到一次,工作线程的下一次sleep就管不到了,相当于一个contine操作; Abort():这个就是相当于一个break操作,工作线程彻底死掉

二、线程池基础

首先,创建和销毁线程是一个要耗费大量时间的过程,另外,太多的线程也会浪费内存资源,所以通过Thread类来创建过多的线程反而有损于性能,为了改善这样的问题 ,.net中就引入了线程池。线程池形象的表示就是存放应用程序中使用的线程的一个集合(就是放线程的地方,这样线程都放在一个地方就好管理了)。CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列,当应用程序想执行一个异步操作时,就调用一个方法,就将一个任务放到线程池的队列中,线程池中代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。

注意:通过线程池创建的线程默认为后台线程,优先级默认为Normal. 

1:GetMaxThreads,GetMinThreads  首先我们肯定好奇线程池到底给我们如何控制线程数,下面就具体的看一看。

using System.Threading;
using System;
class Program
{
    static void Main(string[] args)
    {
        int workerThreads;
        int completePortsThreads;
        ThreadPool.GetMaxThreads(out workerThreads, out completePortsThreads);
        Console.WriteLine("线程池中最大的线程数{0},线程池中异步IO线程的最大数目{1}", workerThreads, completePortsThreads);
        ThreadPool.GetMinThreads(out workerThreads, out completePortsThreads);
        Console.WriteLine("线程池中最小的线程数{0},线程池中异步IO线程的最小数目{1}", workerThreads, completePortsThreads);
    }
}

有的同学可能就要问,我可以将1023设置为10230吗?那么就会有10230个线程帮我做事多好啊,其实不然。

①:我先前的文章也说过,线程很多的话,线程调度就越频繁,可能就会出现某个任务执行的时间比线程调度花费的时间短很多的尴尬局面。

②:我们要知道一个线程默认占用1M的堆栈空间,如果10230个线程将会占用差不多10G的内存空间,我想普通的电脑立马罢工

2:SetMaxTheads,SetMinThreads

当然,默认的线程设置只是一个参考,如果我们处于性能和实际情况确实需要修改也没关系,framework也给我们提供了现成的方法。

using System.Threading;
using System;
class Program
{
    static void Main(string[] args)
    {
        int workerThreads;
        int completePortsThreads;
        ThreadPool.SetMaxThreads(100, 50);
        ThreadPool.SetMinThreads(20, 10);
        ThreadPool.GetMaxThreads(out workerThreads, out completePortsThreads);
        Console.WriteLine("线程池中最大的线程数{0},线程池中异步IO线程的最大数目{1}
", workerThreads, completePortsThreads);
        ThreadPool.GetMinThreads(out workerThreads, out completePortsThreads);
        Console.WriteLine("线程池中最小的线程数{0},线程池中异步IO线程的最小数目{1}
", workerThreads, completePortsThreads);
    }
}

三、通过线程池的工作者线程实现异步

3.1 创建工作者线程的方法

public static bool QueueUserWorkItem (WaitCallback callBack);

public static bool QueueUserWorkItem(WaitCallback callback, Object state);

这两个方法向线程池的队列添加一个工作项(work item)以及一个可选的状态数据。然后,这两个方法就会立即返回。

工作项其实就是由callback参数标识的一个方法,该方法将由线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型,定义为:

public delegate void WaitCallback(Object state);

下面演示如何通过线程池线程来实现异步调用: 

using System;
using System.Threading;

namespace ThreadPoolUse
{
    class Program
    {
        static void Main(string[] args)
        {
            // 设置线程池中处于活动的线程的最大数目
             // 设置线程池中工作者线程数量为1000,I/O线程数量为1000
            ThreadPool.SetMaxThreads(1000, 1000);
            Console.WriteLine("主线程: 一个异步方法将加入队列");
            PrintMessage("主线程开启");

            // 把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法
            ThreadPool.QueueUserWorkItem(asyncMethod);
            Console.Read();
        }

        // 方法必须匹配WaitCallback委托
        private static void asyncMethod(object state)
        {
            Thread.Sleep(1000);
            PrintMessage("asyncMethod异步方法");
            Console.WriteLine("asyncMethod方法的异步线程在工作中...");
        }

        // 打印线程池信息
        private static void PrintMessage(String data)
        {
            int workthreadnumber;
            int iothreadnumber;

            // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
            // 获得的可用I/O线程数量给iothreadnumber变量
            ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);

            Console.WriteLine("{0}
 当前线程Id is {1}
 当前线程是后台线程吗?-- :{2}
 可用辅助线程数:{3}
 可用异步IO线程数: {4}
",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
}

 运行结果:

从结果中可以看出,线程池中的可用的工作者线程少了一个,用去执行回调方法了。

ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object对象作为参数传送到回调函数中,使用和ThreadPool.QueueUserWorkItem(WaitCallback callback)的使用和类似,这里就不列出了。

3.2 协作式取消

.net Framework提供了取消操作的模式, 这个模式是协作式的。为了取消一个操作,首先必须创建一个System.Threading.CancellationTokenSource对象。

下面代码演示了协作式取消的使用,主要实现当用户在控制台敲下回车键后就停止数数方法。 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(1000, 1000);
            Console.WriteLine("主线程运行中...");    
            PrintMessage("Start");
            Run();
            Console.ReadKey();
        }

        private static void Run()
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            // 这里用Lambda表达式的方式和使用委托的效果一样的,只是用了Lambda后可以少定义一个方法。
            // 这在这里就是让大家明白怎么lambda表达式如何由委托转变的
            ////ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000));

            ThreadPool.QueueUserWorkItem(callback, cts.Token);

            Console.WriteLine("按下Enter键取消操作
");
            Console.ReadLine();

            // 传达取消请求
            cts.Cancel();
        }
        
        private static void callback(object state)
        {
            Thread.Sleep(1000);
            PrintMessage("异步方法 Start");
            CancellationToken token =(CancellationToken)state;    
            Count(token, 1000);
        }

        // 执行的操作,当受到取消请求时停止数数
        private static void Count(CancellationToken token,int countto)
        {
            for (int i = 0; i < countto; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("Count 已经被取消");
                    break;
                }

                Console.WriteLine(i);
                Thread.Sleep(300);
            }
            
            Console.WriteLine("Cout 结束");       
        }

        // 打印线程池信息
        private static void PrintMessage(String data)
        {
            int workthreadnumber;
            int iothreadnumber;

            // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
            // 获得的可用I/O线程数量给iothreadnumber变量
            ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);

            Console.WriteLine("{0}
 当前线程Id is {1}
 当前线程是后台线程吗?-- :{2}
 可用辅助线程数:{3}
 可用异步IO线程数: {4}
",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
}

 运行结果:

使用RegisterWaitForSingleObject取消:RegisterWaitForSingleObject返回一个RegisteredWaitHandle类,那么我们就通过RegisteredWaitHandle来动态的控制,比如说停止计数器的运行。

我们知道,如果我们把要执行的任务丢给线程池后,相当于把自己的命运寄托在别人的手上。
①:我们再也不能控制线程的优先级了。
②:丢给线程池后,我们再也不能将要执行的任务取消了。
是的,给别人就要遵守别人的游戏规则,不过RegisterWaitForSingleObject提供了一些简单的线程间交互,因为该方法的第一个参数是
WaitHandle,在VS对象浏览器中,我们发现EventWaitHandle继承了WaitHandle,而ManualResetEvent和AutoResetEvent都继承于
EventWaitHandle,也就是说我们可以在RegisterWaitForSingleObject溶于信号量的概念。

using System.Threading;
using System;
class Program
{
    static void Main(string[] args)
    {
        RegisteredWaitHandle handle = null;
        AutoResetEvent ar = new AutoResetEvent(false);
        //handle = ThreadPool.RegisterWaitForSingleObject(ar, Run1, null, Timeout.Infinite, false);
        //ar.Set();
        
        handle = ThreadPool.RegisterWaitForSingleObject(ar, Run1, null, 2000, false);
        //10s后停止
        Thread.Sleep(10000);
        handle.Unregister(ar);
        Console.WriteLine("小子,主线程要干掉你了。");
        Console.Read();
    }

    static void Run1(object obj, bool sign)
    {
        Console.WriteLine("当前时间:{0}  我是线程{1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
    }
}

四、使用委托实现异步

通过调用ThreadPool的QueueUserWorkItem方法来来启动工作者线程非常方便,但委托WaitCallback指向的是带有一个参数的无返回值的方法,如果我们实际操作中需要有返回值,或者需要带有多个参数, 这时通过这样的方式就难以实现, 为了解决这样的问题,我们可以通过委托来建立工作这线程,下面代码演示了使用委托如何实现异步:

using System;
using System.Threading;

namespace Delegate
{
    class Program
    {
        // 使用委托的实现的方式是使用了异步变成模型APM(Asynchronous Programming Model)
        // 自定义委托
        private delegate string MyTestdelegate();

        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("主线程 Start");

            //实例化委托
            MyTestdelegate testdelegate = new MyTestdelegate(asyncMethod);

            // 异步调用委托
            IAsyncResult result = testdelegate.BeginInvoke(null, null);

            // 获取结果并打印出来
            string returndata = testdelegate.EndInvoke(result);
            Console.WriteLine(returndata);

            Console.ReadLine();
        }
        private static string asyncMethod()
        {
            Thread.Sleep(1000);
            PrintMessage("异步方法");
            return "异步方法完成了";
        }

        // 打印线程池信息
        private static void PrintMessage(String data)
        {
            int workthreadnumber;
            int iothreadnumber;

            // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
            // 获得的可用I/O线程数量给iothreadnumber变量
            ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);

            Console.WriteLine("{0}
 当前线程Id is {1}
 当前线程是后台线程吗?-- :{2}
 可用辅助线程数:{3}
 可用异步IO线程数: {4}
",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
}

运行结果是:

五、任务

同样 任务的引入也是为了解决通过ThreadPool.QueueUserWorkItem中限制的问题,

下面代码演示通过任务来实现异步:

5.1 使用任务来实现异步 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskUse
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("主线程Start");
            // 调用构造函数创建Task对象,
            Task<int> task = new Task<int>(n => asyncMethod((int)n), 10);

            // 启动任务 
            task.Start();
            // 等待任务完成
            task.Wait();
            Console.WriteLine("方法结果是: " + task.Result);
            Console.ReadLine();
        }

        private static int asyncMethod(int n)
        {
            Thread.Sleep(1000);
            PrintMessage("asyncMethod异步方法");

            int sum = 0;
            for (int i = 1; i < n; i++)
            {
                // 如果n太大,使用checked使下面代码抛出异常
                checked
                {
                    sum += i;
                }
            }
            return sum;
        }

        // 打印线程池信息
        private static void PrintMessage(String data)
        {
            int workthreadnumber;
            int iothreadnumber;

            // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
            // 获得的可用I/O线程数量给iothreadnumber变量
            ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);

            Console.WriteLine("{0}
 当前线程Id 是{1}
 当前线程是后台线程吗?-- :{2}
 可用辅助线程数:{3}
 可用异步IO线程数: {4}
",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
}

 运行结果是:

5.2 取消任务

如果要取消任务, 同样可以使用一个CancellationTokenSource对象来取消一个Task.下面代码演示了如何来取消一个任务: 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskUse
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("主线程Start");
            CancellationTokenSource cts = new CancellationTokenSource();

            // 调用构造函数创建Task对象,将一个CancellationToken传给Task构造器从而使Task和CancellationToken关联起来
            Task<int> task = new Task<int>(n => asyncMethod(cts.Token, (int)n), 10);

            // 启动任务 
            task.Start();
            // 延迟取消任务
            Thread.Sleep(3000);
            // 取消任务
            cts.Cancel();
            Console.WriteLine("方法结果是: " + task.Result);
            Console.ReadLine();
        }

        private static int asyncMethod(CancellationToken ct, int n)
        {
            Thread.Sleep(1000);
            PrintMessage("asyncMethod异步方法");

            int sum = 0;
            try
            {
                for (int i = 1; i < n; i++)
                {
                    // 当CancellationTokenSource对象调用Cancel方法时,
                    // 就会引起OperationCanceledException异常
                    // 通过调用CancellationToken的ThrowIfCancellationRequested方法来定时检查操作是否已经取消,
                    // 这个方法和CancellationToken的IsCancellationRequested属性类似
                    ct.ThrowIfCancellationRequested();
                    Thread.Sleep(500);
                    // 如果n太大,使用checked使下面代码抛出异常
                    checked
                    {
                        sum += i;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("异常是:" + e.GetType().Name);
                Console.WriteLine("操作已经被取消了");
            }
            return sum;
        }

        // 打印线程池信息
        private static void PrintMessage(String data)
        {
            int workthreadnumber;
            int iothreadnumber;

            // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
            // 获得的可用I/O线程数量给iothreadnumber变量
            ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);

            Console.WriteLine("{0}
 当前线程Id 是{1}
 当前线程是后台线程吗?-- :{2}
 可用辅助线程数:{3}
 可用异步IO线程数: {4}
",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
}

 运行结果是:

5.3 任务工厂

同样可以通过任务工厂TaskFactory类型来实现异步操作。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskFactory
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(1000, 1000);
            Task.Factory.StartNew(() => PrintMessage("主线程"));
            Console.Read();
        }
        // 打印线程池信息
        private static void PrintMessage(String data)
        {
            int workthreadnumber;
            int iothreadnumber;

            // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
            // 获得的可用I/O线程数量给iothreadnumber变量
            ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);

            Console.WriteLine("{0}
 当前线程Id 是{1}
 当前线程是后台线程吗?-- :{2}
 可用辅助线程数:{3}
 可用异步IO线程数: {4}
",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
}

运行结果:

讲到这里CLR的工作者线程大致讲完了,希望也篇文章可以让大家对线程又有进一步的理解。在后面的一篇线程系列将谈谈CLR线程池的I/O线程。

转载自:http://www.cnblogs.com/zhili/archive/2012/07/18/ThreadPool.html

http://www.cnblogs.com/huangxincheng/archive/2012/03/18/2405039.html

原文地址:https://www.cnblogs.com/lusunqing/p/3210464.html