线程池-调用委托
线程池可以成功的适应于任何需要大量短暂的开销大的资源的情形。我们事先分配一定的资源,将这些资源放入到资源池。每次需要新的资源,只需从池中获取一个,而不用创建一个新的。当该资源不再被使用时,就将其返回到池中。
线程池是受.NET通用语言运行时(CLR)管理的。这意味着每个CLR都有一个线程池实例。ThreadPool类型拥有一个QueueUserWorkItem静态方法。该静态方法接受一个委托,代表用户自定义的一个异步操作。在该方法被调用后,委托会进入到内部队列中。如果池中没有任何线程,将创建一个新的工作线程(worker thread)并将队列中第一个委托放入到该工作线程中。
注意:保持线程中的操作都是短暂的是非常重要的。不要在线程池中放入长时间运行的操作,或者阻塞工作线程。这将导致所有工作线程变得繁忙,从而无法服务用户操作。这会导致性能问题和非常难以调试的错误。
线程池的用途是执行运行时间短的操作。
我们知道 beginvoke就是通过线程的调用来异步的完成一些工作。一般只需要启动它就好,让它一直操作着。例如 用begininvoke修改界面显示,那么就是每次有所变化时它自动的改变界面的显示,因为它在后台执行着。
但是有时候我们需要知道它的结束信息,并且在结束时还有所安排。这时候就需要endinvoke了。
异步编程模型(Asynchronous Programming Model,简称APM)
代码Demo:
using System;
using System.Threading;
在Main方法下面加入以下代码片段:
private delegate string RunOnThreadPool(out int threadId);
private static void Callback(IAsyncResult ar)
{
Console.WriteLine("Starting a callback...");
Console.WriteLine("State passed to a callback:{0}", ar.AsyncState);
Console.WriteLine("Is thread pool thread:{0}", Thread.CurrentThread.IsThreadPoolThread);//确保该线程不是来自线程池
Console.WriteLine("Thread pool worker thread id:{0}", Thread.CurrentThread.ManagedThreadId);//打印出了受管理的线程ID来识别代码是被那个线程执行的
}
private static string Test(out int threadId)
{
Console.WriteLine("Starting...");
Console.WriteLine("Is thread pool thread:{0}", Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(2));
threadId = Thread.CurrentThread.ManagedThreadId;
return string.Format("Thread pool wrker thread id was:{0}", threadId);
}
在Main方法中加入以下代码片段:
int threadId = 0;
RunOnThreadPool poolDelegate = Test;
var t = new Thread(() => Test(out threadId));//线程的构造函数只接受一个无任何返回结果的方法。
t.Start();
t.Join();//等待完成
Console.WriteLine("Thread id:{0}", threadId);
IAsyncResult r = poolDelegate.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");
r.AsyncWaitHandle.WaitOne();
string result = poolDelegate.EndInvoke(out threadId, r);
Console.WriteLine("Thread pool worker thread id:{0}", threadId);
Console.WriteLine(result);
Thread.Sleep(TimeSpan.FromSeconds(2));
工作原理:
定义了一个委托并调用BeginInvoke方法来运行该委托。BeginInvoke方法接受一个回调函数。该回调函数会在异步操作完成后会被调用,并且一个用户自定义的状态会给该回调函数。该状态通常用于区分异步调用。结果,我们得到了一个实现了IAsyncResult接口的result对象。BeginInvoke立即返回了结果,当线程池中的工作线程在执行异步操作是,扔允许我们继续其它工作。当需要异步操作的结果时,可以使用BeginInvoke方法调用返回的result对象。我们可以使用result对象的IsCompleted属性轮询结果。本例子使用的是SsyncWaitHandle属性来等待知道操作完成。当操作完成后,会得到一个结果,可以通过委托调用EndInvoke方法,将IAsyncResult对象传递给委托参数。
注意:EndInvoke方法事实上会等待异步操作完成。调用该方法是非常重要的,因为该方法会将任何未处理的异常抛回到调用线程中。当使用这样异步API时,请确保始终调用了Begin和End方法。
当操作完成后,传递给BeginInvoke方法的回调函数将被放置到线程池中,确切的说是一个工作线程中。如果在Main方法定义的结尾注释掉Thread。Sleep方法调用,回调函数将不会被执行。这是因为当主线程完成后,所有的后台线程会被停止,包括该回调函数。对委托和回调函数的异步调用很可能会被同一个工作线程执行。通过工作线程ID可以容易的看出。