学习重点ThreadPool.SetMinThreads(out workerThreads, out completionPortThreads).这是整个线程池的关键。 而ThreadPool.SetMaxThreads(out workerThreads, out completionPortThreads)这个方法在你的线程总数低于1000时是不需要动它的。因为它的默认值就是1023,1000。然后在实际使用的时候只靠客户端发大量线程来处理问题是不一定能降低实际执行时间的,有时候还和服务器端、数据库有关系。
首先谈一下学习的背景,因为一箱货里有几百件产品。用一条SQL语句来检查这几百件产品是否合格,搜索数据对比规格后出结果耗费的时间在7~12秒的时间。现场的操作环境是一箱一箱货不停的扫描的。7~12秒的处理时间导致两箱货的扫描间隔达到10秒之多这是无法接受的。在这种情况下首先想到的是用多线程同时处理所有产品的数据。
List<System.Threading.Thread> list = new List<System.Threading.Thread>(); foreach (DataRow row in ds.Tables[0].Rows) { //cellname = row[0].ToString(); System.Threading.Thread s1 = new System.Threading.Thread(QueryOCVTime); list.Add(s1); s1.Start(row[0].ToString()); } bool IsOver = true; while (IsOver) { for (int i = 0; i < list.Count; i++) { if (list[i].IsAlive) { System.Threading.Thread.Sleep(50); break; } else if (i == (list.Count - 1)) { IsOver = false; } } }
经过测试处理时间已经降到3~5秒了。但是离需求(1秒以内)还有很大的差距。而且也测试了单线程的执行时间才100毫秒,按逻辑来分析我几乎是同时发出所有的单线程来执行,总的执行时间应该也是100毫秒左右。所以线程都是所以就想到了线程池
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net; using System.Threading; namespace ConsoleApplication1 { class Program { private static List<string> list; private static DateTime dt1; static void Main(string[] args) { int maxworkthreads, maxportthreads, minworkthreads, minportthreads; Console.WriteLine("修改前"); ThreadPool.GetMaxThreads(out maxworkthreads, out maxportthreads); ThreadPool.GetMinThreads(out minworkthreads, out minportthreads); Console.WriteLine(maxworkthreads.ToString() + "\t" + maxportthreads.ToString()); Console.WriteLine(minworkthreads.ToString() + "\t" + minportthreads.ToString()); //public static bool SetMinThreads(int workerThreads, int completionPortThreads); //最关键的修改参数 //workerThreads //这个值决定了同时执行的最大线程数,超过这个数量就要排队。 //这个值不是越大越好,要综合考虑单线程在进行什么样的操作。 ThreadPool.SetMinThreads(500, minportthreads); Console.WriteLine("修改后"); ThreadPool.GetMaxThreads(out maxworkthreads, out maxportthreads); ThreadPool.GetMinThreads(out minworkthreads, out minportthreads); Console.WriteLine(maxworkthreads.ToString() + "\t" + maxportthreads.ToString()); Console.WriteLine(minworkthreads.ToString() + "\t" + minportthreads.ToString()); list = new List<string>(); dt1 = DateTime.Now; for (int i = 0; i < 100; i++) { //开始执行单线程 ThreadPool.QueueUserWorkItem(new WaitCallback(RunThread), i.ToString()); } bool IsOver = true; while (IsOver) { //检测所有单线程是否执行完毕 int maxthreads, threads, workthreads; ThreadPool.GetMaxThreads(out maxthreads, out threads); ThreadPool.GetAvailableThreads(out workthreads, out threads); if ((maxthreads - workthreads) == 0) { IsOver = false; } else { Thread.Sleep(100); } } DateTime dt2 = DateTime.Now; //运行完毕将执行过程打印出来以便于分析 for (int i = 0; i < list.Count; i++) { Console.WriteLine(list[i]); } Console.WriteLine("流程已经运行完毕一共耗时{0}", (dt2 - dt1).TotalSeconds.ToString("0.000")); Console.ReadKey(); } private static void RunThread(object obj) { DateTime dt2 = DateTime.Now; //假定单线程的处理时间是800毫秒 Thread.Sleep(800); DateTime dt3 = DateTime.Now; //记录下线程和执行时间的信息到后面再打印出来,以免执行时间在打印时浪费而影响测试结果 list.Add(string.Format("{0},thread {1}\t,启动时间{2},耗时 {3},总耗时 {4}", obj.ToString(), Thread.CurrentThread.ManagedThreadId, (dt2 - dt1).TotalSeconds.ToString("0.000"), (dt3 - dt2).TotalSeconds.ToString("0.000"), (dt3 - dt1).TotalSeconds.ToString("0.000") ) ); } } }
“public static bool SetMinThreads(int workerThreads, int completionPortThreads);”。这个SetMinThreads(out workerThreads, out completionPortThreads)方法是整个线程池的关键。WorkThreads是指最大活动线程数(即可同时执行的最大线程数),completionPortThreads是指最大IO线程数(即可同时执行的最大IO)。WorkThreads的大小影响整个流程的结束时间,如果单个线程需要10毫秒才能执行完,一共有10个线程并且把WorkThreads设置为5,那么整个流程的完成时间在20毫秒左右。即是单个线程的2倍时间。即执行时间=总线程数/WorkThreads * 单流程时间。
这段测试代码的执行结果是非常理想的。
98,thread 107 ,启动时间0.219,耗时 0.801,总耗时 1.020
99,thread 108 ,启动时间0.225,耗时 0.801,总耗时 1.026
流程已经运行完毕一共耗时1.11
但是在将这段代码移植到实际情况中时结果令人很沮丧。仍然还是要2~3秒才能出结果。仔细查看输出结果不难发现其中的问题
1,thread 6 ,启动时间0.247,耗时 0.309,总耗时 0.556
2,thread 22 ,启动时间0.270,耗时 0.291,总耗时 0.561
......
39,thread 53 ,启动时间0.372,耗时 1.389,总耗时 1.761
40,thread 55 ,启动时间0.372,耗时 1.394,总耗时 1.766
......
50,thread 61 ,启动时间0.374,耗时 1.610,总耗时 1.984
63,thread 63 ,启动时间0.373,耗时 1.677,总耗时 2.050
从上面的输出结果可以看出来在实际的使用环境中,虽然所有线程的启动时间相差是不大的,但是执行时间相差太多了从第1个线程才0.309秒到最后一个线程达到了1.677秒。表明同时执行的线程越多,到后面的线程执行的效率就越慢。导致最后的总执行时间就超出我们的预期了。
这里导致执行效率慢的原因已经不再是线程的问题了。而有可能是数据库的IO或服务器的IO有限制。客户端同时发出大量的请求查找数据,但是在服务端或者在数据库这些同时发出来的线程出现排队的情况了。这个结论是我经过大量测试后得出来的,在后期如果有条件再来验证这个想法。