25.5.2 使用线程池

在面向对象编程中,创建和销毁对象是很费时的,因为创建一个对象要获取内存资源或者其他更多资源。创建线程也是如此,因为Thread也是一个对象。使用一种叫做“池(Pool)”的技术可以优化对象的使用,其原理是预先创建一定数量的对象放在“池”里,在对象被请求时,某个对象被从“池”中取出供使用,使用完毕再重新放回“池”中,等待下一次的请求。使用这种机制尽可能地减少了创建和销毁对象的次数,如果是对于一些很耗资源的对象创建和销毁将更加有效。这是一种“池化资源”的技术。行文至此,可能读者朋友在想,如果有一个线程池就好了,除了选择自己来编写实现这样一个“线程池”以外,.NET Framework已经为我们提供了一个“线程池”供使用,它叫做ThreadPool,同样也位于System.Threading命名空间。

需要注意的是,线程池中的线程均为后台线程,即它们的IsBackground属性为true。这意味着在所有的前台线程都已退出后,ThreadPool中的线程不会让应用程序继续保持运行。

ThreadPool线程池多用于执行一些简单的、耗时短暂的任务,在以下情况下不应使用线程池:

❑由于线程池中的线程均为后台线程,因此当需要创建一个前台线程时不应使用线程池;

❑线程池中的线程都是默认优先级,因此当需要创建具有特定优先级的线程时不应使用线程池;

❑当需要某个任务只和特定的线程关联时,不应该使用线程池,因为无法选择执行任务时使用的是哪个具体线程;

❑当需要中止特定的线程时不应使用线程池,它不提供这个功能;

❑线程执行时间很长,线程池多用于短而多的线程任务。

要使用ThreadPool中的线程,需要使用ThreadPool.QueueUserWorkItem这个静态方法指定线程要调用的方法,该方法有2个重载版本:

❑public static bool QueueUserWorkItem(WaitCallback callBack)

❑public static bool QueueUserWorkItem(WaitCallback callBack,object state)

这两个版本都拥有一个共同的WaitCallback类型参数,WaitCallback是一个委托类型,如下所示。


public delegate void WaitCallback(object state);


可见,该委托十分类似于前文讲的ParameterizedThreadStart委托,其中state参数可以接受从外部传入的参数值,那么就不难发现前文所提到的QueueUserWorkItem方法的两个版本的区别,即第1个版本没有提供传入参数的途径;第2个版本提供了这种途径。接下来使用一段示例代码来说明线程池的使用方法,其中涉及了QueueUserWorkItem方法两种重载版本的使用。注意观察运行结果,从结果可以观察出每个正在运行的线程ID,如代码清单25-9所示。

代码清单25-9 线程池使用示例


using System;

using System.Threading;

namespace ProgrammingCSharp4

{

class AsynchronousSample

{

static void Main(string[]args)

{

ThreadPool.QueueUserWorkItem(Counter);

ThreadPool.QueueUserWorkItem(Counter,"test");

Console.WriteLine(“[线程ID={0}]主线程启动”,

Thread.CurrentThread.ManagedThreadId);

Thread.Sleep(500);

Console.WriteLine(“主线程退出.”);

}

private static void Counter(object state)

{

Console.WriteLine(“开始计数,从1到100:”);

Console.WriteLine(“传入的参数值为:{0}”,state);

for(int counter=0;counter<100;counter++)

{

if(null==state)

{

Console.WriteLine(“[线程ID={0}]{1}”,Thread.CurrentThread.

ManagedThreadId,counter);

}

else

{

Console.WriteLine(“[线程ID={0}]{1}”,Thread.CurrentThread.

ManagedThreadId,state);

}

Thread.Sleep(100);

}

}

}

}


上述代码的运行结果如下:


[线程ID=1]主线程启动

开始计数,从1到100:

传入的参数值为:test

[线程ID=4]test

开始计数,从1到100:

传入的参数值为:

[线程ID=3]0

[线程ID=4]test

[线程ID=3]1

[线程ID=3]2

[线程ID=4]test

[线程ID=3]3

[线程ID=4]test

[线程ID=3]4

[线程ID=4]test

主线程退出.

请按任意键继续……