.NET提供了哪几个定时器类型

一笑奈何 提交于 2020-03-20 20:30:34

分析问题

  在.NET内建类型中,一共为程序员提供了3种定时器:

  1、System.Windows.Forms.Timer类型。

  2、System.Threading.Timer类型。

  3、System.Timers.Timer类型。

  概况来说,这三种类型都实现了定时的功能。程序员通常需要做的是为定时器设置一个间断时间,设置定时到达时的处理方法,然后就可以等待定时器不断地计时和触发处理事件,现在笔者来分别介绍一下这三种类型的特点。

  1、System.Windows.Forms.Timer类型

  从这个定时器的命名空间可以看出,.NET设计这个类型的目的是为了方便程序员在Windows界面上使用定时器。当一个System.Windows.Forms.Timer类型被构造时,当前定时器会和当前线程进行关联。而当定时器的计时到达后,一个定时器消息将被插入到当前线程的消息队列中。当前线程将逐一处理消息队列中的所有消息,并一一派发给各自的处理方法。这样的机制和利用工作者线程进行定时有很大的区别,事实上,System.Windows.Forms.Timer类型并没有涉及多线程的操作,定时器的设置、定时方法的执行都在同一个线程上。

注意

  这就意味着System.Windows.Forms.Timer并不能准确地定时,事实上,当消息阻塞时,定时器的误差将相当大,因为定时器消息只能等排在前面的所有消息处理完后才能得到处理。

  2、System.Threading.Timer类型。

  这个定时器类型的使用相对复杂,但同时它也是相对最优的一个定时器类型。System.Threading.Timer的定时方法将被确定在工作者线程上执行。所有的对象都有一个线程控制,当下一个计时到达时,该线程会负责在线程中获得一个新的工作者线程,用以执行相应的回调方法。其基本使用方法如下所示:

ThreadTimer timer=new Timer(new TimerCallback(ThreadTimerHandler),null,Timeout.Infinite,Timeout.Infinite,ThreadTimer.Change(INTERVAL,INTERVAL));

  3、System.Timers.Timer类型。

  这是一个相对比较旧的类型。它和System.Threading.Timer一样,也可以由工作者线程来执行回调方法,但同时它也可以在IDE的环境中被拖放到窗体控件上,这个时候它的行为非常类似于System.Windows.Forms.Timer类型,在消息过多时其定时并不准确。

   下面的代码示例是一个使用三种定时器的示例,这是一个窗体程序。

  (1)首先设计其界面部分,界面允许用户在三种定时器中选择任意一种,并且进行启动、休眠、终止等操作,并且界面上需要添加一个Windows Timer控件。界面布局如下图所示。

  

using System.Windows.Forms;
using System;

namespace WindowsFormsApplication1
{
    
    public partial class Timers : Form
    {
        private const int INTERVAL = 1000;//1 second
        private System.Timers.Timer timersTimer = new System.Timers.Timer(INTERVAL);
        private System.Threading.Timer threadTimer = null;
        //这个委托用来在工作者线程中访问UI线程中创建的控件
        private delegate void OutputDelegate(DateTime time);
        public Timers()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 启动定时器
        /// </summary>
        private void Start_Click(object sender, EventArgs e)
        { 
            //先保证所有的定时器停止
            WindowsFormsTimerStop();
            TimersTimerStop();
            ThreadTimerStop();

            if (FormsTimer.Checked)
            {
                WindowsFormsTimerStart();
            }
            else if (SystemTimersTimer.Checked)
            {
                TimersTimerStart();
            }
            else
            {
                ThreadTimerStart();
            }

        }

        /// <summary>
        /// 使当前线程休眠一段时间
        /// </summary>
        private void Sleep_Click(object sender, EventArgs e)
        { 
            //睡眠5 second
            outPut.Text += "现在开始睡眠5秒";
            System.Threading.Thread.Sleep(5 * 1000);
        }

        /// <summary>
        /// 停止所有定时器
        /// </summary>
        private void Stop_Click(object sender, EventArgs e)
        { 
            //保证所有定时停止
            WindowsFormsTimerStop();
            TimersTimerStop();
            ThreadTimerStop();
        }

        /// <summary>
        /// 清空输出
        /// </summary>
        private void Reset_Click(object sender, EventArgs e)
        {
            outPut.Text = string.Empty;
        }

        #region 三个定时器的启动与停止

        /// <summary>
        /// 启动Windows.Forms.Timer定时器
        /// </summary>
        private void WindowsFormsTimerStart()
        {
            WindowsFormsTimer.Interval = INTERVAL;
            WindowsFormsTimer.Tick += TimerHandler;
            WindowsFormsTimer.Start();
        }

        /// <summary>
        /// 停止Windows.Forms.Timer定时器
        /// </summary>
        private void WindowsFormsTimerStop()
        {
            WindowsFormsTimer.Stop();
        }



        /// <summary>
        /// 启动Timers.Timer定时器
        /// </summary>
        private void TimersTimerStart()
        {
            timersTimer.Elapsed += TimerHandler;
            //把当前窗体控件赋给Timers.Timer的同步对象属性,
            //以保证其回调方法能访问当前UI线程的控件
            timersTimer.SynchronizingObject = this;
            timersTimer.Start();
        }

        /// <summary>
        /// 停止Timers.Timer定时器
        /// </summary>
        private void TimersTimerStop()
        {
            timersTimer.Stop();
        }

        /// <summary>
        /// 启动Threading.Timer定时器
        /// 当初始化结束后该定时器就立即启动
        /// </summary>
        private void ThreadTimerStart()
        {
            threadTimer = new System.Threading.Timer(new System.Threading.TimerCallback(ThreadTimerHandler), null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            threadTimer.Change(INTERVAL, INTERVAL);
        }

        /// <summary>
        /// 如果Threading.Timer没有停止,则停止
        /// 这里停止使用的是把间隔时间设置为无限大
        /// Threading.Timer接口和其他两个定时器类型略有不同
        /// </summary>
        private void ThreadTimerStop()
        {
            if (threadTimer != null)
            {
                threadTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            }
        }

        #endregion

        #region 回调方法及其他

        /// <summary>
        /// 实际执行Threading.Timer的方法
        /// </summary>
        /// <param name="time"></param>
        private void ThreadTimerHandlerUI(DateTime time)
        {
            outPut.Text += time.ToString() + "System.Threading.Timer到时了\r\n";
        }

        /// <summary>
        /// Threading.Timer的回调方法,需要使用BeginInvoke方法来访问UI线程中的控件
        /// </summary>
        /// <param name="state">状态参数</param>
        private void ThreadTimerHandler(object state)
        {
            BeginInvoke(new OutputDelegate(ThreadTimerHandlerUI), DateTime.Now);
        }

        /// <summary>
        /// 回调方法
        /// </summary>
        /// <param name="sender">事件发送者</param>
        /// <param name="e">事件参数</param>
        private void TimerHandler(object sender, EventArgs e)
        {
            outPut.Text += DateTime.Now.ToString() + sender.ToString() + "到时了\r\n";
        }

        #endregion
    }

    
}

  编译并运行上述代码,选择想要测试的定时器,单击启动按钮,就会有相应的输出来监控定时输出。休眠按钮会导致但前UI线程休眠5秒,这是为了试验各个定时器的不同计时方式。为了检查几个定时器在处理回调时的不同,下面我们分别分析它们的输出。

  1、System.Windows.Forms.Timer定时输出。

  

  如上图所示,当主UI线程休眠5秒时,定时器就被中断了,第二个回调方法和第三个回调方法中间正好相差5秒。这就意味着定时有了严重的误差。这是因为定时器的计时和窗体在同一个线程之中,所有某处的阻塞导致定时器无法正确计时。

  2.System.Timers.Timer定时输出。

  

  请读者注意代码中Timers.Timer设置了同步块对象属性:SynchronizedObject。这使得它能够在UI线程上运行,而同时它也会被阻塞影响。分析输出(如上图所示),当主线程睡眠时,System.Timers.Timer的回调方法不能被执行,但和System.Windows.Forms.Timer不同的是,他并没有遗漏任何应该触发的定时器事件,在主线程休眠恢复后,休眠期间应该被执行的4次定时回调事件全部被执行。所以虽然System.Timers.Timer不是一个准确的定时器,但它却不会遗漏节拍。

  3、System.Threading.Timer定时输出。

  

  正如笔者先前介绍的,System.Threading.Timer可以说是一个相对最优的定时器,如上图所示,在主UI线程休眠时期间,它并没有遗漏任何节拍,并且每次回调都按期执行,这是因为System.Threading.Timer的回调方法在一个工作者线程上执行。

注意

  System.Timers.Timer也可以在工作者线程上执行,这时候其工作结果基本和System.Threading.Timer相同。但System.Timers.Timer仍然使用了比较旧的计时机制,笔者建议不要使用System.Timers.Timer。

答案

  .NET的内建类型中有三个定时器类型,分别是:

  1、System.Windows.Forms.Timer类型。

  2、System.Timers.Timer类型。

  3、System.Threading.Timer类型。

  System.Windows.Forms.Timer用于窗体设计,并且运行在窗体线程之中,这导致了其计时不准确和遗漏节拍的特点;System.Threading.Timer类型的每个计时回调都在一个工作者线程上执行,其计时相对准确;System.Timers.Timer可以视为System.Threading.Timer的一个包装,其类型设计相对古老,笔者不建议读者使用该定时器。

  

  

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!