详解C#中的定时器Timer类及其垃圾回收机制
关于C#Timer类 在C#里关于定时器类就有3个
C#Timer使用的方法1.定义在System.Windows.Forms里
C#Timer使用的方法2.定义在System.Threading.Timer类里 "
C#Timer使用的方法3.定义在System.Timers.Timer类里
下面我们来具体看看这3种C#Timer用法的解释:
(1)System.Windows.Forms.Timer
应用于WinForm中的,它是通过Windows消息机制实现的,类似于VB或Delphi中的Timer控件,内部使用API SetTimer实现的。它的主要缺点是计时不精确,而且必须有消息循环,Console Application(控制台应用程序)无法使用。
(2)System.Timers.Timer
和System.Threading.Timer非常类似,它们是通过.NET Thread Pool实现的,轻量,计时精确,对应用程序、消息没有特别的要求。
(3)System.Timers.Timer还可以应用于WinForm,完全取代上面的Timer控件。它们的缺点是不支持直接的拖放,需要手工编码。
C#Timer用法实例
使用System.Timers.Timer类 System.Timers.Timert= newSystem.Timers.Timer(10000); //实例化Timer类,设置间隔时间为10000毫秒; t.Elapsed+= newSystem.Timers.ElapsedEventHandler(theout); //到达时间的时候执行事件; t.AutoReset=true; //设置是执行一次(false)还是一直执行(true); t.Enabled=true; //是否执行System.Timers.Timer.Elapsed事件; publicvoidtheout( objectsource, System.Timers.ElapsedEventArgse) { MessageBox.Show("OK!"); }
Timer的垃圾回收机制
通常我们需要定时执行一段任务的时候,我们就需要定时器,这时我们就可以使用c#System.Threading空间中的Timer定时器;他是个异步定时器,时间到时每次都是在线程池中分配一个线程去执行任务。下面我们来看一个有趣的例子:
classProgram { staticvoidMain(string[]args) { Timertimer=newTimer(TimerCallback,null,0,2000); Console.ReadLine(); } privatestaticvoidTimerCallback(objecto) { Console.WriteLine("inTimerCallbackmethod"); GC.Collect(); } }
当我们在debug模式下运行该段程序时,正如我们期盼的那样程序会每隔2秒钟执行该方法,打印出"inTimerCallbackmethod”,而在release模式下执行的时候,只执行一次该方法,字符串只打印一次。在这里我们在调用TimerCallback方法时,强制执行垃圾回收器,说明在release模式下,垃圾回收器执行回收算法时,首先假设所有对象都是可回收的,当将Timer对象赋值给变量t后,t没有在被引用,因此也就没有变量引用Timer对象,所以垃圾收集这时会回收Timer对象。那么为什么在debug模式下却能够运行能,这跟c#编译器的优化方式有关,在release模式下编译器做了相关的优化操作。而在debug模式下,timer对象的生成期是方法的结束,这样做也是为了调试的方便。要不然在调试时,我们执行到Timertimer=newTimer()后想看timer的值时,已经被垃圾回收器给回收了,这是我们不期望看到的结果,编译器如何处理的,我们可以看看编译器在release模式下和debug模式下对上面的代码编译后生成的IL对比我们既知结果。
release模式编译生成的IL:
.methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged { .entrypoint //Codesize32(0x20) .maxstack8 IL_0000:ldnull IL_0001:ldftnvoidGCTest.Program::TimerCallback(object) IL_0007:newobjinstancevoid[mscorlib]System.Threading.TimerCallback::.ctor(object, nativeint) IL_000c:ldnull IL_000d:ldc.i4.0 IL_000e:ldc.i40x7d0 IL_0013:newobjinstancevoid[mscorlib]System.Threading.Timer::.ctor(class[mscorlib]System.Threading.TimerCallback, object, int32, int32) IL_0018:pop IL_0019:callstring[mscorlib]System.Console::ReadLine() IL_001e:pop IL_001f:ret }//endofmethodProgram::Main
debug模式下生成的IL:
methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged { .entrypoint //Codesize33(0x21) .maxstack4 .localsinit([0]class[mscorlib]System.Threading.Timertimer) IL_0000:nop IL_0001:ldnull IL_0002:ldftnvoidGCTest.Program::TimerCallback(object) IL_0008:newobjinstancevoid[mscorlib]System.Threading.TimerCallback::.ctor(object, nativeint) IL_000d:ldnull IL_000e:ldc.i4.0 IL_000f:ldc.i40x7d0 IL_0014:newobjinstancevoid[mscorlib]System.Threading.Timer::.ctor(class[mscorlib]System.Threading.TimerCallback, object, int32, int32) IL_0019:stloc.0 IL_001a:callstring[mscorlib]System.Console::ReadLine() IL_001f:pop IL_0020:ret }//endofmethodProgram::Main
从生成的IL中我们可以看出在debug模式下,生成IL比在release模式下多了19行红色字体的IL指令码,该指令码的作用是将15行生成的引用Timer对象的栈上的变量存放到局部变量0中。所以使得在debug模式下该t还被引用,不能够回收Timer对象,所以也能出现我们期盼的结果,那么如何在两种模式下都能得到我们期盼的结果呢。我们可以如下操作。
正确的代码:
classProgram { staticvoidMain(string[]args) { Timertimer=newTimer(TimerCallback,null,0,2000); Console.ReadLine(); timer.Dispose(); } privatestaticvoidTimerCallback(objecto) { Console.WriteLine("inTimerCallbackmethod"); GC.Collect(); } }
这时不管是在release模式下还是debug模式下,都会每隔2秒钟调用我们的回调方法。