接上文 多线程编程学习笔记——线程同步(一)
接上文 多线程编程学习笔记——线程同步(二)
接上文 多线程编程学习笔记——线程同步(三)
创建多线程操作是非常昂贵的,所以每个运行时间非常短的操作,创建多线程进行操作,可能并不能提高效率,反而降低了效率。
如果你有非常多的执行时间非常短的操作,那么适合作用线程池来提高效率,而不是自行创建多线程。
线程池,就是我们先分配一些资源到池子里,当我们需要使用时,则从池子中获取,用完了,再放回池子里。
.NET中的线程池是受CLR管理的,TheadTool类有一个QueueUserWorkItem静态方法,这个静态方法接受一个委托,代表用户自定义的一个异步操作,在这个方法被调用之后,委托会进入到内部队列中,如果池中没有线程,则创建一个工作线程,把第一个委托放入工作线程。如果继续放入委托,则池创建新的工作线程,直到工作线程数量达到上限。这时再放入委托,则不会创建新的工作线程,而是在队列中等待,直到有空闲的工作线程。
当线程池中所有操作都完成,而且没有新任务操作时,线程池会释放长时间不用的资源。
注意:放入线程池中的操作需要的时间要短,不要把需要长时间运行的操作放入线程池中,或阻塞工作线程。这将导致性能问题和非常难以调用的问题。
在ASP.NET中使用线程池要当心,ASP.NET中的线程池是一个共用线程池,如果线程池中的工作线程都用完了,则会造成WEB服务器对正常的HTTP请求无法提供服务。
一、 线程池中调用委托
1.代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ThreadPoolDemo { public delegate string ThreadPoolRun(out int threadId); class Program { static void Main(string[] args) { Console.WriteLine("开始测试线程池-委托。。。"); int threadId = 0; ThreadPoolRun poolDele = RunThread; var t = new Thread(() => RunThread(out threadId)); t.Start(); t.Join(); Console.WriteLine("线程ID {0} ",threadId); IAsyncResult r = poolDele.BeginInvoke(out threadId, Callback, "在线程池中同步调用回调函数"); string result = poolDele.EndInvoke(out threadId, r); Console.WriteLine("线程池中工作线程ID :{0}", threadId); Console.WriteLine("返回结果:{0}",result); Thread.Sleep(2000); Console.Read(); } private static void Callback(IAsyncResult r) { Console.WriteLine("开始调用回调函数。。。"); Console.WriteLine("回调函数此时的状态 :{0}",r.AsyncState); Console.WriteLine("调用此回调函数的线程是否在线程池 :{0}", Thread.CurrentThread.IsThreadPoolThread); Console.WriteLine("调用此回调函数的线程在线程池在的ID :{0}", Thread.CurrentThread.ManagedThreadId); } private static string RunThread(out int threadId) { Console.WriteLine("开始工作。。。"); Console.WriteLine("调用此回调函数的线程是否在线程池 :{0}", Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(2)); threadId = Thread.CurrentThread.ManagedThreadId; return string.Format("此线程在线程池在的ID :{0}", threadId); } } }
2.程序执行结果如下图。
上面的程序运行时,我们首先创建线程来执行委托操作,然后调用委托的BeginInvoke来执行回调方法,这个回调函数会在异步操作完成之后会被调用,并且会把一个自定义的值传给这个回调函数,最后我们会得到一个实现了IAsyncResult接口的result对象,当线程池的工作线程在进行工作时,允许我们继续其他操作。我们可以轮询result对象的IsCompleted属性,确定操作是否完成。也可以调用EndInvoke将IAsyncResult传给委托参数。
二、 线程池中放入异步操作
1.代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ThreadPoolDemo { class Program { static void Main(string[] args) { Console.WriteLine("开始测试线程池-QueueUserWorkItem。。。"); const int x = 1; const int y = 2; string workState = "工作状态 2"; ThreadPool.QueueUserWorkItem(AsyncOper); Thread.Sleep(1000); ThreadPool.QueueUserWorkItem(AsyncOper,"同步状态"); Thread.Sleep(1000); ThreadPool.QueueUserWorkItem(status => { Console.WriteLine("操作状态 {0} ", status); Console.WriteLine("线程池中工作线程ID :{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(TimeSpan.FromSeconds(2)); },"工作状态"); ThreadPool.QueueUserWorkItem(_ => { Console.WriteLine("操作结果x+y= {0} ,{1}", x+y,workState); Console.WriteLine("线程池中工作线程ID :{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(TimeSpan.FromSeconds(2)); }, "工作状态"); Thread.Sleep(2000); Console.Read(); } private static void AsyncOper(object status) { Console.WriteLine("操作状态 :{0} ",status??"null"); Console.WriteLine("工作线程在线程池在的ID :{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); } } }
2.执行结果如下。程序执行了两次。
程序首先定义了一个AsyncOper方法,然后使用QueueUserWorkItem将这个方法放入线程池中,然后再次放入一个AsyncOper方法,不过这次给方法调用传一个对象。
代码中的调用Thread.sleep方法,是为了让线程池中的工作线程为新操作重用。请注意打印出来的ThradId,如果ThreadID一样则证明两个操作重用了同一个工作线程。