在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
原 http://blog.csdn.net/BusyDonkey/article/details/5327665 在C#里现在有3个Timer类:
这三个Timer我想大家对System.Windows.Forms.Timer已经很熟悉了,唯一我要说的就是这个Timer在激发Timer.Tick事件的时候,事件的处理函数是在程序主线程上执行的,所以在WinForm上面用这个Timer很方便,因为在From上的所有控件都是在程序主线程上创建的,那么在Tick的处理函数中可以对Form上的所有控件进行操作,不会造成WinForm控件的线程安全问题。 1、Timer运行的核心都是System.Threading.ThreadPool 在这里要提到ThreadPool(线程池)是因为,System.Threading.Timer 和System.Timers.Timer运行的核心都是线程池,Timer每到间隔时间后就会激发响应事件,因此要申请线程来执行对应的响应函数,Timer将获取线程的工作都交给了线程池来管理,每到一定的时间后它就去告诉线程池:“我现在激发了个事件要运行对应的响应函数,麻烦你给我向操作系统要个线程,申请交给你了,线程分配下来了你就运行我给你的响应函数,没分配下来先让响应函数在这儿排队(操作系统线程等待队列)”,消息已经传递给线程池了,Timer也就不管了,因为它还有其他的事要做(每隔一段时间它又要激发事件),至于提交的请求什么时候能够得到满足,要看线程池当前的状态:
那么上面提到了线程池有最大工作线程数,其实还有最小空闲线程数,那么这两个关键字是什么意思呢:
下面是我给的例子,这个例子让线程池申请800个线程,其中设置最大工作线程数为500,800个线程任务每个都要执行100000000毫秒目的是让线程不会释放,并且让用户选择,是否预先申请500个空闲线程免受那半秒钟的延迟时间,其结果可想而知当线程申请到500的时候,线程池达到了最大工作线程数,剩余的300个申请进入漫长的等待时间:
2、System.Threading.Timer 谈完了线程池,就可以开始讨论Timer,这里我们先从System.Threading.Timer开始,System.Threading.Timer的作用就是每到间隔时间后激发响应事件并执行相应函数,执行响应函数要向线程池申请线程,当然申请中会遇到一些情况在上面我们已经说了。值得注意的一点就是System.Threading.Timer在创建对象后立即开始执行,比如System.Threading.Timer timer = new System.Threading.Timer(Excute, null, 0, 10);这句执行完后每隔10毫秒就执行Excute函数不需要启动什么的。下面就举个例子,我先把代码贴出来:
这个例子包含了两个Timer的类UnSafeTimer和SafeTimer,两个类的代码的大致意思就是使用Timer每隔10毫秒就执行Excute函数,Excute函数会显示当前执行的次数,在80次的时候通过timer.Dispose()让Timer停止不再激发响应事件。 首先我们来分析下UnSafeTimer class UnSafeTimer static void Excute(object obj) lock (mylock) if (c == 80) if (c < 80) if (flag) public static void Init(int p_sleep, bool p_flag) 你可以执行试一试,在输入是否执行安全方法的时候选N,等待时间1000,申请了80个线程后Timer剩余激发的线程选N,本来想在80次的时候停下来,可是你会发现直到执行到660多次之后才停下来(具体看机器配置),申请前80个线程的时间为10532毫秒,反正执行的次数大大超出了限制的80次,回头想想让Timer不在激发事件的方法是调用timer.Dispose(),难不成是Dispose有延迟?延迟的过程中多执行了500多次?那么我们再来做个试验,我们在申请了80个线程后Timer剩余激发的线程选y,请耐心等待结果,在最后你会发现执行时间还是660次左右,这很显然是不合理的,如果Dispose有延迟时间造成所执行500多次,那么加长80次后面每个线程的申请时间在相同的延迟时间内申请的线程数应该减少,因为后面500多个线程每个线程都要执行1000毫秒,那么势必有些线程会去申请新的线程有半秒钟的等待时间(你会发现申请了80个线程后Timer剩余激发的线程选y明显比选n慢得多,就是因为这个原因),所以看来不是因为Dispose造成的。 那么会是什么呢?我们这次这样选在输入是否执行安全方法的时候选N,等待时间500,申请了80个线程后Timer剩余激发的线程选N 那么会是什么呢?我们这次这样选在输入是否执行安全方法的时候选N,等待时间50,申请了80个线程后Timer剩余激发的线程选N 我们发现随着每次任务等待时间的减少多执行的次数也在减少,最关键的一点我们从图中可以看到,前80次任务申请的时间也在减少,这是最关键的,根据上面线程池所讲的内容我们可以归纳出:每次任务的等待时间直接决定了前80个任务的执行时间,因为等待时间越短,每个任务就可以越快执行完,那么80个任务中就有越多的任务可以用到前面任务执行完后释放掉的线程,也就有越多的任务不必去线程池申请新的线程避免多等待半秒钟的申请时间,而Timer并不会去关心线程池申请前80个任务的时间长短,只要它没有执行到timer.Dispose(),它就会每隔10毫秒激发一次响应时间,不管前80次任务执行时间是长还是短,timer都在第80次任务才执行Dispose,执行Dispose后timer就不会激发新的事件了,但是如果前80次任务申请的时间越长,那么timer就会在前80次任务申请的时间内激发越多响应事件,那么线程池中等待队列中就会有越多的响应函数等待申请线程,System.Threading.Timer没有机制取消线程池等待队列中多余的申请数,所以导致等待时间越长,80次后执行的任务数越多。 由此只用timer.Dispose()来终止Timer激发事件是不安全的,所以又写了个安全的执行机制: class SafeTimer static bool flag = true; static void Excute(object obj) lock (mylock) i++; if (i == 80) Thread.Sleep(1000);//模拟花时间的代码 public static void Init() 安全类中我们用了个bool类型的变量flag来判断当前是否执行到80次了,执行到80次后将flag置为false,然后timer.Dispose,这时虽然任务还是要多执行很多次但是由于flag为false,Excute函数一开始就做了判断flag为false会立即退出,Excute函数80次后相当于就不执行了。 3、System.Timers.Timer 在上面的例子中我们看到System.Threading.Timer很不安全,即使在安全的方法类,也只能让事件响应函数在80次后立刻退出让其执行时间近似于0,但是还是浪费了系统不少的资源。 所以本人更推荐使用现在介绍的System.Timers.Timer,System.Timers.Timer大致原理和System.Threading.Timer差不多,唯一几处不同的就是:
那么我们来看个例子:
这个例子和System.Threading.Timer差不多,这里也分为:安全类SafeTimer和不安全类UnSafeTimer,原因是 timer.Stop()有少许的延迟时间有时任务会执行到81~83,但是就算是不安全方法也就最多多执行几次,不像System.Threading.Timer多执行上百次... 所以我这里还是推荐大家使用System.Timers.Timer |
请发表评论