通常我们需要定时执行一段任务的时候,我们就需要定时器,这时我们就可以使用c# System.Threading空间中的 Timer定时器;他是个异步定时器,时间到时每次都是在线程池中分配一个线程去执行任务。下面我们来看一个有趣的例子:
class Program { static void Main(string[] args) { Timer timer = new Timer(TimerCallback,null,0,2000); Console.ReadLine(); } private static void TimerCallback(object o) { Console.WriteLine("in TimerCallback method"); GC.Collect(); } }
当我们在debug模式下运行该段程序时,正如我们期盼的那样程序会每隔2秒钟执行该方法,打印出"in TimerCallback method”,而在release模式下执行的时候,只执行一次该方法,字符串只打印一次。在这里我们在调用TimerCallback方法时,强制执行垃圾回收器,说明在release模式下,垃圾回收器执行回收算法时,首先假设所有对象都是可回收的,当将Timer对象赋值给变量t后,t没有在被引用,因此也就没有变量引用Timer对象,所以垃圾收集这时会回收Timer对象。那么为什么在debug模式下却能够运行能,这跟c#编译器的优化方式有关,在release模式下编译器做了相关的优化操作。而在debug模式下,timer对象的生成期是方法的结束,这样做也是为了调试的方便。要不然在调试时,我们执行到Timer timer = new Timer()后想看timer的值时,已经被垃圾回收器给回收了,这是我们不期望看到的结果,编译器如何处理的,我们可以看看编译器在release模式下和debug模式下对上面的代码编译后生成的IL对比我们既知结果。
release模式编译生成的IL:
1 .method private hidebysig static void Main(string[] args) cil managed 2 { 3 .entrypoint 4 // Code size 32 (0x20) 5 .maxstack 8 6 IL_0000: ldnull 7 IL_0001: ldftn void GCTest.Program::TimerCallback(object) 8 IL_0007: newobj instance void [mscorlib]System.Threading.TimerCallback::.ctor(object, 9 native int) 10 IL_000c: ldnull 11 IL_000d: ldc.i4.0 12 IL_000e: ldc.i4 0x7d0 13 IL_0013: newobj instance void [mscorlib]System.Threading.Timer::.ctor(class [mscorlib]System.Threading.TimerCallback, 14 object, 15 int32, 16 int32) 17 IL_0018: pop 18 IL_0019: call string [mscorlib]System.Console::ReadLine() 19 IL_001e: pop 20 IL_001f: ret 21 } // end of method Program::Main
debug模式下生成的IL:
1 method private hidebysig static void Main(string[] args) cil managed 2 { 3 .entrypoint 4 // Code size 33 (0x21) 5 .maxstack 4 6 .locals init ([0] class [mscorlib]System.Threading.Timer timer) 7 IL_0000: nop 8 IL_0001: ldnull 9 IL_0002: ldftn void GCTest.Program::TimerCallback(object) 10 IL_0008: newobj instance void [mscorlib]System.Threading.TimerCallback::.ctor(object, 11 native int) 12 IL_000d: ldnull 13 IL_000e: ldc.i4.0 14 IL_000f: ldc.i4 0x7d0 15 IL_0014: newobj instance void [mscorlib]System.Threading.Timer::.ctor(class [mscorlib]System.Threading.TimerCallback, 16 object, 17 int32, 18 int32) 19 IL_0019: stloc.0 20 IL_001a: call string [mscorlib]System.Console::ReadLine() 21 IL_001f: pop 22 IL_0020: ret 23 } // end of method Program::Main
从生成的IL中我们可以看出在debug模式下,生成IL比在release模式下多了19行红色字体的IL指令码,该指令码的作用是将15行生成的引用Timer对象的栈上的变量存放到局部变量0中。所以使得在debug模式下该t还被引用,不能够回收Timer对象,所以也能出现我们期盼的结果,那么如何在两种模式下都能得到我们期盼的结果呢。我们可以如下操作。
正确的代码:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Timer timer = new Timer(TimerCallback,null,0,2000); 6 7 Console.ReadLine(); 8 timer.Dispose(); 9 } 10 11 private static void TimerCallback(object o) 12 { 13 Console.WriteLine("in TimerCallback method"); 14 15 GC.Collect(); 16 17 18 } 19 }
这时不管是在release模式下还是debug模式下,都会每隔2秒钟调用我们的回调方法。