一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

.NET中的6种定时器的基本用法和特点实用指南

时间:2026-09-01 16:20:02 编辑:袖梨 来源:一聚教程网

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“.NET中的6种定时器的基本用法和特点”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

实际处理时,.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 FrameworkASP.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, EnabledSynchronizingObject,这些属性允许设置回调的执行方式。此外,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需考虑自己处理异常。相比之前的定时器来说,有下面几个特点:

  • 没有callback 来绑定事件;
  • 在这个场景下,不会发生重入,只允许有一个消费者,不允许同一个PeriodicTimer在不同的地方同时WaitForNextTickAsync,不需自己做排他锁来实现不能重入;
  • 结合项目来看,异步化。之前的 timer 的 callback 都是同步的,采用新 timer 能够采用异步方法,避免了编写 Sync over Async 代码;
  • 结合项目来看,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中几种定时器各自都有其适用场景和不足,但都不兼容高精度计时。了解这些有助于我们在开发过程中选择合适定时器,避免遇到问题后被动地替换解决方案。

  • (链接已移除)

到此这篇关于.NET中有多少种定时器的文章就介绍到这了,更多相关.NET定时器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!

您可能感兴趣的文章:
  • .NET中实现高精度定时器的思路
  • .NET中6种定时器的用法与特点详解
  • ASP.NET 定时器回调方法的重入
  • .NET Framework中定时器timer的单线程与多线程采用讲解
  • asp.net中Timer无刷新定时器的实现方法

热门栏目