.Net中的计时器Timer你真的会用吗?
作者:互联网
一、Timer存在的问题
注意:下面演示的Timer都是指的System.Threading的Timer类。
先看以下代码:
//实例化定时器,并以1000毫秒/次的频率执行
Timer timer = new(Test, null, 0, 1000);
//第1个参数指“在调用指定的回调方法之前延迟的时间量”
//第2个参数指“回调方法的调用之间的时间间隔”
timer.Change(0, 0);
//定时执行的业务
void Test(object? state)
{
//获取当前的线程ID
int id = Thread.CurrentThread.ManagedThreadId;
//获取一个随机数
Random random = new Random();
int roundId = random.Next(1, 10000);
Console.WriteLine($"{DateTime.Now} 线程ID = {id}_{roundId} 开始执行");
//模拟耗时的操作
Thread.Sleep(3000);
Console.WriteLine($"{DateTime.Now} 线程ID = {id}_{roundId} 结束执行");
}
解释一下上面的代码:
创建一个定时器timer对象,以1000毫秒的频率循环执行回调方法Test,在方法Test中模拟一个耗时的操作,每次会耗费3000毫秒。为了区分每次执行,通过线程ID和一个随机数作为唯一ID。
程序执行结果如下:
通过上图可以看出,第一次执行Test回调方法还没结束,就开始执行了第2次定时任务,在很多业务场景中,这是不允许的,这会导致很多不必要的麻烦,这也是在我们使用Timer的时候,需要重点关注的。
二、正确的做法
步骤:
1、在创建定时器Timer时,指定Change()的第2个参数为Timeout.Infinite,这样,定时器就只会出发一次。
2、在回调方法中,再次调用Change(),同样需要指定参数为Timeout.Infinite。
Timer _timer = null;
//实例化定时器,并以1000毫秒/次的频率执行
_timer = new(Test, null, 0, 1000);
//第1个参数指返回前timer,不需要等待
//第2个参数指不“超时时间无穷大”,如果第一个循环任务一直没完成,一直等待下去
_timer.Change(0, Timeout.Infinite);
void Test(object? state)
{
//获取当前的线程ID
int id = Thread.CurrentThread.ManagedThreadId;
//获取一个随机数
Random random = new Random();
int roundId = random.Next(1, 10000);
Console.WriteLine($"线程ID = {id} 开始执行,{roundId}");
//模拟耗时的操作
Thread.Sleep(3000);
Console.WriteLine($"线程ID = {id} 结束执行,{roundId}");
//这里需要再次启动下一个定时任务
_timer.Change(0, Timeout.Infinite);
}
执行的结果:
通过上图可以看出,每次定时任务执行完后,才会继续执行下一个定时任务。
三、Timer的释放
Timer对象垃圾回收时,它的终结代码告诉线程池取消计数器,使它不再触发。所以,在使用Timer对象时,要确定有一个变量在保持Timer对象的存活,否则对你的回调方法的调用就会停止。
四、多种类型的定时器区别
1、System.Threading的Timer类
这个就是上文讨论的定时器,也是最好的定时器,它是通过线程池去处理的,多线程的(通过上图可以看出,每次的线程ID都不一样),推荐使用。
2、System.Windows.Forms的Timer类
单线程的定时器,不会存在多线程并发的问题。
3、System.Windows.Threading的DispatcherTimer类
这个类是System.Windows.Forms的Timer类在Silverlight和WPF应用程序中的等价物。
4、Windows.UI.Xaml的DispatcherTimer类
这个类是System.Windows.Forms的Timer类在Windows.Store应用程序中的等价物。
5、System.Timers的Timer类
这个定时器本质上是System.Threading的Timer类的扩展类,Microsoft使它在设计器(Winform等项目)中更容易使用。
但是,这个类是好多年前,Microsoft还没有理清线程处理和计时器的时候添加到FCL中的,所以存在一定的缺陷(我也没遇到过)。
所以,强烈建议不要使用这个Timer,推荐使用System.Threading.Timer类。除非,你真的必须要用它。
标签:定时器,System,Timer,线程,timer,计时器,Net,ID 来源: https://www.cnblogs.com/myblog2021/p/16683796.html