.NET中有多少种定时器

2025-12-13 0 600

.NET中至少有6种定时器,每一种定时器都有它的用途和特点。根据定时器的应用场景,可以分为UI相关的定时器和UI无关的定时器。本文将简单介绍这6种定时器的基本用法和特点。

UI定时器

.NET中的UI定时器主要是WinForm、WPF以及WebForm中的定时器。分别为:

  • System.Windows.Forms.Timer
  • System.Windows.Threading.DispatcherTimer
  • System.Web.UI.Timer

通常情况下,WinForm、WPF中的定时器是在UI线程上执行回调函数,因此可以直接访问UI元素。由于WinForm、WPF支持单线程单元模型(Single-Thread Apartment,STA),定时器间隔事件是在UI线程上触发,因此,不用担心线程安全问题。
System.Web.UI.Timer是通过Javascript定时器和服务端异步回调实现,也是单线程的。

请注意,这里说的是通常情况,后边介绍System.Windows.Threading.DispatcherTimer时会提到在非UI线程创建DispatcherTimer时也无法直接访问UI元素。

System.Windows.Forms.Timer

System.Windows.Forms.Timer针对WinForm应用进行了优化,是只能在WinForm上使用的定时器。这个定时器是针对单线程环境设计的,是在UI线程上处理定时任务。
它要求用户代码有可用的UI消息泵,定时任务须在UI线程上运行,或者跨线程通过Invoke或者BeginInvoke封送(marshal)到UI线程上运行。其优点是使用简单,只需通过给Interval属性赋值来设置时间间隔,并注册Tick事件处理定时任务。其缺点是精度不高,精度为55毫秒,也就是Interval赋值小于55时,也是55毫秒触发一次定时任务。

public partial class TimerFrom : Form
{
  private System.Windows.Forms.Timer digitalClock;
  private void TimerFrom_Load(object sender, EventArgs e)
  {
    digitalClock = new System.Windows.Forms.Timer();//创建定时器
    digitalClock.Tick += new EventHandler(HandleTime);//注册定时任务事件
    digitalClock.Interval = 1000;//设置时间间隔
    digitalClock.Enabled = true;
    digitalClock.Start(); //开启定时器
  }
  public void HandleTime(Object myObject, EventArgs myEventArgs)
  {
    labelClock.Text = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss\");
  }
  private void frmTimerDemo_FormClosed(object sender, FormClosedEventArgs e)
  {
    digitalClock.Stop();//停止定时器
    digitalClock.Dispose();
  }
}  

System.Windows.Threading.DispatcherTimer

System.Windows.Threading.DispatcherTimer是WPF中的定时器,它是基于Dispatcher对象的(并不是基于UI线程的)。DispatcherTimer的定时任务是像其他操作一样放在Dispatcher队列上,其执行操作时间依赖于队列中其他任务及其优先级,因此,DispatcherTimer不保证在时间间隔发生时准确执行,只保证不会在时间间隔发生前执行。

Dispatcher为特定线程维护工作项(操作)的优先级队列,在线程上创建Dispatcher对象时,它成为唯一可以关联该线程的Dispatcher对象,WPF中,DispatcherObject只能被与之关联的Dispatcher对象访问,也就是非UI线程中无法直接访问UI元素(WPF中的UI元素都是派生自DispatcherObject)

此外,DispatcherTimer不像System.Windows.Forms.Timer那样只在UI线程上创建才能触发Tick事件,它在非UI线程下创建也可以触发Tick事件,此时访问UI元素也需要通过Invoke或者BeginInvoke封送(marshal)到UI线程上运行。其优点也是简单易用,适合在UI线程上执行任务或触发事件,缺点是精度不准确,可能存在延迟。

private void Dt_Tick(object sender, EventArgs e)
{
  Dispatcher.BeginInvoke((Action)delegate ()
  {
    text1.Text = DateTime.Now.ToString();
  });
  Console.WriteLine(DateTime.Now.ToString());
}

private void Button_Click(object sender, RoutedEventArgs e)
{
  Task.Run(() =>{
    DispatcherTimer dt = new DispatcherTimer();
    dt.Tick += Dt_Tick;
    dt.Interval = TimeSpan.FromSeconds(1);
    dt.Start();
    Dispatcher.Run();
  });
}

上述代码中,DispatcherTimer是非UI线程中创建,定时任务中访问UI元素text1,需要通过Invoke或者BeginInvoke封送(marshal)到UI线程上运行,而Console.WriteLine则可以直接运行。

System.Web.UI.Timer

System.Web.UI.Timer是仅适用于.NET Framework的ASP.NET组件。通过Javascript定时器和服务端异步回调实现。每次触发定时器时,只能执行一个异步回调方法,而其他的异步回调方法需要等待前一个异步回调方法执行完毕后才能执行。这样可以保证在任意时刻只有一个异步回调方法在执行,避免了多线程并发执行的问题。

UI无关定时器

从 .NET 6开始,UI无关定时器有三个:

  • System.Threading.Timer
  • System.Timers.Timer
  • System.Threading.PeriodicTimer(.NET 6+)

System.Threading.Timer

System.Threading.Timer是最基础轻量的定时器,它将定期在线程池线程上执行单个回调方法。在创建定时器对象时必须指定回调方法,并且后续不能修改,同时也可以指定定时器回调开始执行的时间以及时间间隔。定时器创建后可以通过Change方法修改回调开始执行的时间以及时间间隔。该定时器的优点是轻量,精度相对较高,与Windows操作系统时钟精度一致,大约15毫秒。但因为是基于线程池的,所以在任务执行时间较长或者线程池过载时,会出现延迟。其缺点是使用不太方便,定时器创建后无法修改回调方法。

var stateTimer = new
var autoEvent = new AutoResetEvent(false);
Timer(CheckStatus, autoEvent, 1000,250);

private int invokeCount=0;

public void CheckStatus(Object stateInfo)
{
  AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
  Console.WriteLine(\"{0} Checking status {1,2}.\",DateTime.Now.ToString(\"h:mm:ss.fff\"),(++invokeCount).ToString());

  if(invokeCount == 10)
  {
    invokeCount = 0;
    autoEvent.Set();
  }
}

System.Timers.Timer

System.Timers.Timer在内部使用System.Threading.Timer,并公开了更多的属性,如AutoReset,Enabled或SynchronizingObject,这些属性允许配置回调的执行方式。此外,Tick事件允许注册多个处理程序。因此,一个定时器可以触发多个处理程序。还可以在计时器启动后更改处理程序。与System.Threading.Timer相似,其优点也是精度相对较高,与Windows操作系统时钟精度一致,大约15毫秒。因为默认(或者SynchronizingObject=null时)是基于线程池的,所以在任务执行时间较长或者线程池过载时,会出现延迟。但使用要更简便一些。

public partial class TimerFrom : Form
{
  private System.Timers.Timer timer;
  private void TimerFrom_Load(object sender, EventArgs e)
  {
    // 支持注册多个处理程序
    timer.Elapsed += (sender, e) => { label1.Text = DateTime.Now.ToLongTimeString(); };
    timer.Elapsed += (sender, e) => { Console.WriteLine(DateTime.Now.ToLongTimeString()); };
    //自定义回调执行的方式(指定对象所在的线程),SynchronizingObject=null时在线程池上执行
    timer.SynchronizingObject = this;
    timer.AutoReset = true;
    timer.Start();
  }
}

本例中将SynchronizingObject属性设置为Form对象,因此Elapsed的处理程序在UI线程上执行,可以直接修改label1.Text,如果SynchronizingObject属性为null,处理程序则是在线程池线程上执行,修改label1.Text时需要通过Invoke或者BeginInvoke封送(marshal)到UI线程上运行。

System.Threading.PeriodicTimer

System.Threading.PeriodicTimer是 .NET 6中引入的定时器。它能方便地使用异步方式,它没有Tick事件,而是提供WaitForNextTickAsync方法处理定时任务。通常是使用While循环结合CancellationToken一起使用。和CancellationToken一起用的时候需要注意,如果CancellationToken被取消的时候会抛出一个OperationCanceledException需要考虑自己处理异常。相比之前的定时器来说,有下面几个特点:[1]

  1. 没有callback来绑定事件;
  2. 不会发生重入,只允许有一个消费者,不允许同一个PeriodicTimer在不同的地方同时WaitForNextTickAsync,不需要自己做排他锁来实现不能重入;
  3. 异步化。之前的 timer 的 callback 都是同步的,使用新 timer 可以使用异步方法,避免了编写 Sync over Async 代码;
  4. Dispose 之后,实例就无法使用,并且 WaitForNextTickAsync 始终返回 false。

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
using (var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)))
{
try
{
while (await timer.WaitForNextTickAsync(cts.Token))
{
await Task.Delay(3000);
Console.WriteLine($\”ThreadId is {Thread.CurrentThread.ManagedThreadId} — Time is {DateTime.Now:HH:mm:ss}\”);
}
}
catch (OperationCanceledException)
{
Console.WriteLine(\”Operation cancelled\”);
}
}

小结

我们在开发过程中遇到的坑往往不是技术本身的坑,而是我们滥用没有掌握的技术导致的,在有多种技术方案可选的时候,通常只关注技术的优点,忽略了技术适用场景及其局限性。.NET中几种定时器各自都有其适用场景和不足,但都不支持高精度计时。了解这些有助于我们在开发过程中选择合适定时器,避免遇到问题后被动地替换解决方案。


  1. https://xie.infoq.cn/article/6aa23b6850abddf717a6c9fc9

作者:czwy

出处:https://www.cnblogs.com/czwy/p/17862702.html

版权:本作品采用「署名-相同方式共享 4.0 国际」许可协议进行许可。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 .NET中有多少种定时器 https://www.zuozi.net/36517.html

python-爬虫
上一篇: python-爬虫
常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务