在多线程的程序中,经常会出现两种情况。一种情况下,应用程序中的线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应;而另外一种情况则是线程平常都处于休眠状态,只是周期性地被唤醒。这里分析及介绍
.Net Framework
中
ThreadPool class
来对付第一种情况,相应地也会谈到
QueueUserWorkItem
方法和
WaitCallback
委托。而使用
Timer
(
System.Threading.Timer or System.Windows.Forms.Timer
)来对付第二种情况,可以参考《
System.Threading.Timer类的TimerCallback 委托
》。
1. ThreadPool
介绍(
From MSDN
)
ThreadPool class
提供了一个线程池,该线程池可用于发送工作项、处理异步
I/O
、代表其他线程等待以及处理计时器。线程池允许在后台运行多个工作,而不需要为每个任务频繁地创建和销毁单独的线程,从而减少了开销。
线程池通过为应用程序提供一个由系统管理的辅助线程池使您可以更为有效地使用线程。一个线程监视排到线程池的若干个等待操作的状态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。
也可以将与等待操作不相关的工作项排列到线程池。若要请求由线程池中的一个线程来处理工作项,请调用
QueueUserWorkItem
方法。此方法将对将被从线程池中选定的线程调用的方法或委托的引用用作参数。一个工作项排入队列后就无法再取消它。
计时器(
System.Threading.Timer
)队列中的计时器以及已注册的等待操作也使用线程池。它们的回调函数也会排列到线程池。关于这部分的内容,可以参考《
System.Threading.Timer类的TimerCallback 委托
》。
线程池在首次创建
ThreadPool
类的实例时被创建。线程池具有每个可用处理器
25
个线程的默认限制,这可以使用
mscoree.h
文件中定义的
CorSetMaxThreads
来更改。每个线程使用默认的堆栈大小并按照默认的优先级运行。每个进程只能具有一个操作系统线程池。
2. ThreadPool.QueueUserWorkItem
方法
QueueUserWorkItem
方法将指定的方法排入队列以便执行,并指定包含该方法所用数据的对象,此方法在有线程池线程变得可用时执行。
public static bool QueueUserWorkItem(
WaitCallback callBack,
object state
);
如果将方法成功排入队列,则为
true
;否则为
false
。如果排入队列的方法仅需要单个数据项,可以将数据项强制转换为类型
Object
。如果该方法需要多个复杂数据,则必须定义包含这些数据的类。如果没有参数传入,则可以调用
QueueUserWorkItem(WaitCallback callBack)
重载。
ThreadPool
提供的公共方法都是
static
方法,因此也不需要生成
ThreadPool
对象。通过
QueueUserWorkItem
方法在线程池中添加一个工作项目后,目前没有提供简单的方法取消。你不必建立咨监线程,只需要把相应的函数或方法依托
WaitCallback
委托传递给
ThreadPool.QueueUserWorkItem()
方法即可。而线程的创建、管理和运行等等都由系统自动完成,这就是
ThreadPool
的优点。
3. WaitCallback
委托
WaitCallback
委托声明线程池要执行的回调方法,回调方法的声明必须与
WaitCallback
委托声明具有相同的参数。
WaitCallback
表示要在
ThreadPool
线程上执行的回调方法。创建委托,方法是将回调方法传递给
WaitCallback
构造函数。您的方法必须具有此处所显示的签名。通过将
WaitCallback
委托传递给
ThreadPool.QueueUserWorkItem
来将任务排入队列以便执行。您的回调方法将在某个线程池线程可用时执行。
如果要将信息传递给回调方法,请创建包含所需信息的对象,并在将任务排入队列以便执行时将它传递给
QueueUserWorkItem
。每次执行您的回调方法时,
state
参数都包含此对象。
通过将一个方法打包到
WaitCallback
委托中,然后将该委托传递给
ThreadPool.QueueUserWorkItem
静态方法,在线程池中对任务进行排队。
4. Demo application using ThreadPool class
codeproject.com
上有一个不错的
Demo: Proper Threading in Winforms .Net, written by Shawn Cicoria
,并附有
Source code
。
在上述
Demo
程序中,不仅提供了使用
ThreadPool
线程池的方法,而且还演示了
Thread
和
ThreadStart
委托的方法。
private void button1_Click(object sender, System.EventArgs e)
{
ShowProgressDelegate showProgress = new ShowProgressDelegate(ShowProgress);
int imsgs = 100;
//One Way... Using ThreadPool
if ( cbThreadPool.Checked )
{
object obj = new object[] { this, showProgress, imsgs };
WorkerClass wc = new WorkerClass();
bool rc = ThreadPool.QueueUserWorkItem( new WaitCallback (wc.RunProcess), obj);
EnableButton( ! rc );
}
else
{
//another way.. using straight threads
WorkerClass wc = new WorkerClass( this, showProgress, new object[] { imsgs } );
Thread t = new Thread( new ThreadStart(wc.RunProcess));
t.IsBackground = true; //make them a daemon - prevent thread callback issues
t.Start();
EnableButton ( false );
}
}
另外,上述
Demo
程序中还调用
Windows form
控件的
BeginInvoke
方法:在创建控件的基础句柄所在线程上,用指定的参数异步执行指定委托。
BeginInvoke
方法在不同的线程池线程上回调指定的委托,实际上就是发起后台的调用来放置一条消息到
windows form
的消息循环(
message loop
)中。
public IAsyncResult BeginInvoke(Delegate, object[]);
***
Any questions about the demo application, please leave message below. I will try to explain it. Thanks.
References:
1, MSDN, ThreadPool, QueueUserWorkItem and WaitCallback
2, Shawn Cicoria, Proper Threading in Winforms .Net,
http://www.codeproject.com/csharp/winformthreading.asp