详解C#中的定时器Timer类及其垃圾回收机制
作者:go,go 发布时间:2021-07-21 10:57:39
关于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.Timer t =
new System.Timers.Timer(10000);
//实例化Timer类,设置间隔时间为10000毫秒;
t.Elapsed +=
new System.Timers.ElapsedEventHandler(theout);
//到达时间的时候执行事件;
t.AutoReset = true;
//设置是执行一次(false)还是一直执行(true);
t.Enabled = true;
//是否执行System.Timers.Timer.Elapsed事件;
public void theout(
object source,
System.Timers.ElapsedEventArgs e)
{
MessageBox.Show("OK!");
}
Timer的垃圾回收机制
通常我们需要定时执行一段任务的时候,我们就需要定时器,这时我们就可以使用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:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 32 (0x20)
.maxstack 8
IL_0000: ldnull
IL_0001: ldftn void GCTest.Program::TimerCallback(object)
IL_0007: newobj instance void [mscorlib]System.Threading.TimerCallback::.ctor(object,
native int)
IL_000c: ldnull
IL_000d: ldc.i4.0
IL_000e: ldc.i4 0x7d0
IL_0013: newobj instance void [mscorlib]System.Threading.Timer::.ctor(class [mscorlib]System.Threading.TimerCallback,
object,
int32,
int32)
IL_0018: pop
IL_0019: call string [mscorlib]System.Console::ReadLine()
IL_001e: pop
IL_001f: ret
} // end of method Program::Main
debug模式下生成的IL:
method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 33 (0x21)
.maxstack 4
.locals init ([0] class [mscorlib]System.Threading.Timer timer)
IL_0000: nop
IL_0001: ldnull
IL_0002: ldftn void GCTest.Program::TimerCallback(object)
IL_0008: newobj instance void [mscorlib]System.Threading.TimerCallback::.ctor(object,
native int)
IL_000d: ldnull
IL_000e: ldc.i4.0
IL_000f: ldc.i4 0x7d0
IL_0014: newobj instance void [mscorlib]System.Threading.Timer::.ctor(class [mscorlib]System.Threading.TimerCallback,
object,
int32,
int32)
IL_0019: stloc.0
IL_001a: call string [mscorlib]System.Console::ReadLine()
IL_001f: pop
IL_0020: ret
} // end of method Program::Main
从生成的IL中我们可以看出在debug模式下,生成IL比在release模式下多了19行红色字体的IL指令码,该指令码的作用是将15行生成的引用Timer对象的栈上的变量存放到局部变量0中。所以使得在debug模式下该t还被引用,不能够回收Timer对象,所以也能出现我们期盼的结果,那么如何在两种模式下都能得到我们期盼的结果呢。我们可以如下操作。
正确的代码:
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer(TimerCallback,null,0,2000);
Console.ReadLine();
timer.Dispose();
}
private static void TimerCallback(object o)
{
Console.WriteLine("in TimerCallback method");
GC.Collect();
}
}
这时不管是在release模式下还是debug模式下,都会每隔2秒钟调用我们的回调方法。


猜你喜欢
- 文章来源:aspcn 作者:孙雯简单的WEB服务器一个简单的WEB服务器将由列表9.2这样构建.当然,还必须要对方法和回应事件进行改进.简单
- 目录一、事出有因二、解决方案困境三、柳暗花明,终级解决方案第一种实现方案第二种实现方案第三种实现方案四、引发的思考一、事出有因最近有一个场景
- 我就废话不多说了,大家还是直接看代码吧~package com.zejian.annotationdemo; import java.lan
- 下载gradle直接百度gradle,然后点击链接进去就可以找到,这里附上下载链接:gradle下载安装gradle##解压下载下来的zip
- 简介我们在开发web应用的时候,有时候为了适应浏览器大小的调整,需要动态对页面的组件进行位置的调整。这时候就会用到flow layout,也
- class Dirctonary { &nbs
- 本文讲述了在Java中如何创建和结束线程的最基本方法,只针对于Java初学者。一些高级知识如线程同步、调度、线程池等内容将会在后续章节中逐步
- 前言我们在前面介绍AssignAnalyzer时,对AssignAnalyzer.letInit(DiagnosticPosition, V
- 点击图标进入指定浏览器。只需在onCreate()方法里添加如下代码:String url = "http://tiger-kfp
- /* * 获取当前的手机号 &nb
- 基本步骤三数取中在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。在此我们采用三数取中法,也就是取左端、中间
- 前言通过前面这篇文章Android串口通讯SerialPort的使用详情已经基本掌握了串口的使用,那么不经想问自己,到底什么才是串口通讯呢?
- 熟悉Android的朋友们都知道,不管是微博客户端还是新闻客户端,都离不开列表组件,可以说列表组件是Android数据展现方面最
- 在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。但是在通过了Ap
- 上一次接触到编码的知识,还是上大学的时候,那时候学的是通信工程专业,有关编码的内容,不记得是在通信原理还是信息论与编码里面学到的了。却依然记
- 本文实例为大家分享了Java身份证号码校验工具类的具体代码,供大家参考,具体内容如下import java.text.ParseExcept
- Python是一种广泛使用的解释型、高级编程、通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年。可以视之为一种改良(加入一些其他
- 本文实例讲述了Android编程基于重力传感器实现横竖屏放向切换功能。分享给大家供大家参考,具体如下:最近项目中用到了vr视频播放,因为自己
- 需要5个类:1.实体类:Person.java2.抽象类:SQLOperate.java(封装了对数据库的操作)3.助手类:DBOpenHe
- 本文实例为大家分享了Android实现手机多点触摸画圆的具体代码,供大家参考,具体内容如下静态效果图:(多个手指按下和抬起的状态)代码实现部