展示线程池如何工作于大量的异步操作,以及它与创建大量单独的线程的方式有什么不同。
大量创建线程(消耗内存、CPU系统资源,请观察)
使用线程池:执行的时间更长,但对系统资源消耗要小的多
using System; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; namespace Test { class Program { private static void UseThreads(int count) { //通过CountdownEvent可以在主线程中线程池中的任务运行,主线程要等待线程池中的任务完成之后才能继续 using (var countdown = new CountdownEvent(count))//这里给CountdownEvent指定计数为500 { for (int i = 0; i < count; i++) { //使用普通线程 var t1 = new Thread(() => { //CurrentThreadInfo("UseThreads()的"); Thread.Sleep(TimeSpan.FromMilliseconds(100)); countdown.Signal();//每次循环CountdownEvent在这里计数减1,一直减少到0 }); t1.Start(); } countdown.Wait();//主线程一开始在这里停止并等待,CountdownEvent计数减少到0的时候,这里主线程开始执行 } } private static void UseThreadPool(int count) { using (var countdown = new CountdownEvent(count)) { for (int i = 0; i < count; i++) { //使用线程池 ThreadPool.QueueUserWorkItem(_ => { //CurrentThreadInfo("UseThreadPool()的"); Thread.Sleep(TimeSpan.FromMilliseconds(100)); countdown.Signal(); }); } countdown.Wait(); } } /// <summary> /// 向线程池放入异步操作 /// </summary> static void Main() { //CurrentThreadInfo("主线程Main()的"); int count = 500; CodeTimer.Initialize(); int iteration = 2; CodeTimer.Time("UseThreads", iteration, () => { Stopwatch sw = new Stopwatch(); sw.Start(); UseThreads(count); sw.Stop(); Console.WriteLine("使用简单线程耗时:{0} - 计数:{1}", sw.ElapsedMilliseconds, sw.ElapsedTicks); }); CodeTimer.Time("UseThreadPool", iteration, () => { Stopwatch sw2 = new Stopwatch(); sw2.Start(); UseThreadPool(count); sw2.Stop(); Console.WriteLine("使用线程池中耗时:{0} - 计数:{1}", sw2.ElapsedMilliseconds, sw2.ElapsedTicks); }); Console.WriteLine("执行完成"); Console.ReadLine(); } /// <summary> /// 当前线程信息 /// </summary> private static void CurrentThreadInfo(string name) { StringBuilder builder = new StringBuilder(); builder.AppendLine(""); builder.AppendLine(String.Format("{0}线程Id: {1}", name, Thread.CurrentThread.ManagedThreadId)); builder.AppendLine(String.Format("{0}是否使用线程池: {1}", name, Thread.CurrentThread.IsThreadPoolThread)); builder.AppendLine(String.Format("{0}是否后台线程: {1}", name, Thread.CurrentThread.IsBackground)); builder.AppendLine(String.Format("{0}线程状态: {1}", name, Thread.CurrentThread.ThreadState.ToString())); builder.AppendLine(String.Format("{0}当前时间: {1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))); builder.AppendLine(""); Console.WriteLine(builder.ToString()); } } }
工作原理:
使用普通线程
当主程序启动时,创建了很多不同的线程,每个线程都运行一个操作。该操作打印出线程ID并阻塞线程100毫秒。结果我们创建了500个线程,全部并行运行这些操作。虽然在我的机器上的总耗时是300毫秒,但是所有线程消耗了大量的操作系统资源。
使用线程池
然后我们使用了执行同样的任务,只不过部位每个操作创建一个线程,儿将他们放入到线程池中。然后线程池开始执行这些操作。线程池在快结束时创建更多的线程,但是仍然花费了更多的时间,在我机器上是12秒。我们为操作系统节省了内存和线程数,但是为此付出了更长的执行时间。