知识大全 Net框架类库中定时器类的使用

Posted

篇首语:没关系,天空越黑,星星越亮。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Net框架类库中定时器类的使用相关的知识,希望对你有一定的参考价值。

Net框架类库中定时器类的使用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  不论在客户端应用程序还是服务器组件(包括窗口服务)定时器通常扮演一个重要的角色 写一个高效的定时器驱动型可管理代码要求对程序流程有一个清晰的理解及掌握 NET线程模型的精妙之处 NET框架类库提供了三种不同的定时器类 System Windows Forms Timer System Timers Timer 和System Threading Timer 每个类为不同的场合进行设计和优化 本文章将研究这三个类并让你理解如何及何时应该使用哪一个类

  Microsoft? Windows?里的定时器对象当行为发生时允许你进行控制 定时器一些最常用的地方就是有规律的定时启动一个进程 在事件之间设置间隔 及当进行 图形工作时维护固定的动画速度(而不管处理函数的速度) 在过去 对于使用Visual Basic?的开发者来说 定时器甚至用来模拟多任务

  正如你所期望的那样 对于你需要应对的不同场合微软为你装备了一些工具 在 NET框架类库中有三种不同的定时器类 System Windows Forms Timer System Timers Timer 和System Threading Timer 头两个类出现在Visual Studio? NET的工具箱窗口 这两个定时器控件都允许你直接把它们拖拽到Windows窗体设计器或组件类设计器上 如果你不小心 这就是麻烦的开始

  Visual Studio NET工具箱上的Windows窗体页和组件页(见Figure )都有定时器控件 非常容易的错误地使用它们当中的一个 或者更糟糕的是 根本意识不到它们的不同 仅当目标是Windows窗体设计器时才使用Windows窗体页上的定时器控件 这个控件将在你的窗体上放置一个Systems Windows Forms Timer类的实例 像工具箱上的其它控件一样 你可以让Visual Studio NET处理其生成或者你自己手动的实例和初始化这个类

  Figure 定时器控件

  在组件页上的定时器控件可以被安全的用在任何类中 这个控件创建了一个System Timers Timer类的实例 如果你正在使用Visual Studio NET工具箱 无论是Windows窗体设计器还是组件类设计器你都可以安全的使用这个类 在Visual Studio NET中当你设计一个派生于System ComponentModel Component的类时使用组件类设计器 System Threading Timer类不出现在Visual Studio NET工具箱窗口上 它稍微有点复杂但提供了一个更高级别的控件 稍后你会在本文章中看到

  Figure 例子程序

  让我们首先研究System Windows Forms Timer和System Timers Timer类 这两个类有着非常相似的对象模型 稍后我将探索更加高级的System Threading Timer类 Figure 是我将在整个文章引用的例子程序的一个屏幕快照 这个应用程序将会让你获得对这几个定时器类的清晰的理解 你可以从本文章的开始链接处下载完整的代码并试验它

  System Windows Forms Timer

  如果你在找一个节拍器 你已经走错了地方了 这个定时器类引发的定时器事件是同你的窗口应用程序的其余代码相同步的 这意味着正在执行的代码从来不会被这个定时器类的实例所抢占(假设你不调用Application DoEvents) 就像一个典型窗体程序里的其它代码一样 任何驻留在一个定时器事件处理函数(指的是该类型的定时器类)中的代码也是使用应用程序的UI线程所执行 在空闲时候 该UI线程同样要对应用程序的窗体消息队列中的所有消息进行负责 这不仅包括由这个定时类引发的消息 也包括窗体API消息 无论何时你的程序不忙于做其它事情时该UI线程就处理这些消息

  在Visual Studio NET之前如果你写过Visual Basic代码 你可能知道在一个窗口应用程序里当正在执行一个事件处理函数时让你的UI线程去响应其它窗体消息的唯一方法就是调用Application DoEvents方法 就像Visual Basic一样 从 NET框架中调用Application DoEvents能够产生许多问题 Application DoEvents产生了对UI消息泵的控制 让你对所有未处理的事件进行处理 这能够改变我刚才提到的所期望的执行路径 如果为了处理由该定时器类产生的定时器事件而在你的代码中有一个Application DoEvents的调用 你的程序流程可能会被打断 这会产生不希望的行为并使调试困难

  运行例子程序就会使这个定时器类的行为变得清楚 单击程序的Start按钮 接着单击Sleep按钮 最后单击Stop按钮 将会产生下面的输出结果

  System Windows Forms Timer Started @ : : PM > Timer Event @ : : PM on Thread UIThread > Timer EVENT @ : : PM on Thread: UIThread > Timer Event @ : : PM on Thread: UIThreadSleeping for ms > Timer Event @ : : PM on Thread: UIThreadSystem Windows Forms Timer Stopped @ : : PM

  例子程序设置System Windows Forms Timer类的间隔属性为 毫秒 正如你所看到的 当UI线程正在睡眠( 秒)期间如果定时器事件处理函数仍然继续捕捉定时器事件的话 当睡眠线程再次被唤醒的时候应该有 个定时器事件被显示——在UI线程睡眠时每秒钟一个 然而 当UI线程在睡眠时定时器却保持挂起状态

  对System Windows Forms Timer的编程不能再简单了——它有一个非常简单和可直接编程的接口 Start和Stop方法实际上提供了一个设置使能属性的改变方法(其本身是对Win ?的SetTimer和KillTimer功能的一个包装) 我刚才提到的间隔属性 名字本身就说明了问题 即使技术上你可以设置间隔属性低到 毫秒 但你应该知道在 NET框架文档中指出这个属性大约精确到 毫秒(假定UI线程对于处理是可用的)

  捕捉由System Windows Forms Timer类实例引发的事件是通过感知一个标准的EventHandler委托的标记事件来处理的 就像下面的代码片断所示

  System Windows Forms Timer tmrWindowsFormsTimer = new System Windows Forms Timer();tmrWindowsFormsTimer Interval = ;tmrWindowsFormsTimer Tick += new EventHandler(tmrWindowsFormsTimer_Tick);tmrWindowsFormsTimer Start(); private void tmrWindowsFormsTimer_Tick(object sender System EventArgs e) //Do something on the UI thread   System Timers Timer

   NET框架文档指出System Timers Timer类是一个服务器定时器 是为多线程环境进行设计和优化 该定时器类的实例能够被多个线程安全地访问 不像System Windows Forms Timer System Timers Timer缺省的 将在一个工作者线程上调用你的定时器事件处理函数 该工作者线程是从公共语言运行时(CLR)线程池中获得 这意味着在你的逝去的时间处理函数代码中必须遵从Win 编程的黄金规则 除了创建该控件实例的线程之外 一个控件的实例从来不被任何其它的线程所访问

  System Timers Timer提供了一个简单的方法处理这样的困境——暴露一个公共的SynchronizingObject属性 把该属性设置为一个窗体实例(或者窗体上的一个控件)将保证你的事件处理函数代码运行在SynchronizingObject被实例化的同一个线程里

  如果你使用了Visual Studio NET工具箱 Visual Studio NET自动的设置SynchronizingObject属性为当前的窗体实例 首先它设定该定时器的SynchronizingObject属性使其在功能上同System Windows Forms Timer类一样 对于大部分功能 的确是这样 当操作系统通知System Timers Timer类所允许的定时时间已过去 定时器使用SynchronizingObject Begin Invoke方法在一个线程上去执行事件委托 该线程是创建SynchronizingObject的线程 事件处理函数将被阻塞直到UI线程能够处理它 然而不像System Windows Forms Timer类一样 该事件最终仍然能够被引发 像你在Figure 中看到的 当UI线程不能够处理时System Windows Forms Timer不会引发事件 可是当UI线程可用时System Timers Timer却会排队等候处理

  Figure 是如何使用SynchronizingObject属性的例子 使用例子程序并通过选择System Timers Timer的radio按钮你可以分析这个类 并按照执行System Windows Forms Timer类行为的同样顺序运行该类 这样就会产生Figure 的输出结果

  正如你所看到的 它不会跳过一个跳动——即使UI线程在睡眠 在每一个事件间隔就有一个时间消失事件处理会被排队执行 因为UI线程在睡眠 所以当UI线程一旦被唤醒例子程序就会列出 个定时器事件( 到 )并能够处理处理函数

  正如我早先提到的 System Timers Timer类成员非常类似与System Windows Forms Timer 最大的区别就在与System Timers Timer类是对Win 可等待定时对象的一个包装 并在工作者线程上产生一个时间片消失事件而不是在UI线程上产生一个时间标记事件 时间片消失事件必须与一个同ElapsedEventHandler委托像匹配的事件处理函数相连接 事件处理函数接受一个ElapsedEventArgs类型的参数

  除了标准的EventArgs成员 ElapsedEventArgs类暴露了一个公共的SignalTime属性 它包含了一个精确的定时器时间片消失的时间 因为这个类支持不同线程的访问 除了时间消失事件所在的线程 应该相信它的Stop方法能够被其它线程所调用 这会潜在的导致消失事件被引发即使其Stop方法已经被调用 你可以把SignalTime和Stop方法调用的时间进行比较来解决这个问题

  System Timers Timer也提供了AutoReset属性来决定当时间片消失事件引发后是继续进行还是只这一次 要记住在定时器开始后重设间隔属性会导致当前计数为

  比如 设置了一个 秒的间隔 在间隔被改变为 秒时 秒已经过去了 那么下一个定时器事件将会在上一个定时器事件 秒后发生

  System Threading Timer

  第三个定时器类来自System Threading名字空间 我愿意说这是所有定时器类中最好的一个 但这会引起误导 举一个例子 我惊讶的发现对于驻留在System Threading名字空间的这个类天生就不是线程安全的 (很明显 这不意味着它不能以线程安全的方式使用) 这个类的可编程接口同其它两个类也不一致 它稍微有点麻烦

  不像我开始描述的两个定时器类 System Threading Timer有四个重载构造函数 就像下面这样

  public Timer(TimerCallback callback object state long dueTime long period);public Timer(TimerCallback callback object state UInt dueTime UInt period);public Timer(TimerCallback callback object state int dueTime int period);public Timer(TimerCallback callback object state TimeSpan dueTime TimeSpan period);

  第一个参数(callback)要求一个TimerCallback的委托 它指向一个方法 该方法具有下面的结构

  public void TimerCallback(object state);

  第二个参数(state)可以为空或者是包含程序规范信息的对象 在每一个定时器事件被调用时该state对象作为一个参数传递给你的定时回调函数 记住定时回调功能是在一个工作者线程上执行的 所以你必须确保访问state对象的线程安全

  第三个参数(dueTime)让你定义一个引发初始定时器事件的时间 你可指定一个 立即开始定时器或者阻止定时器自动的开始 你可以使用System Threading Timeout Infinite常量

  第四个参数(period)让你定义一个回调函数被调用的时间间隔(毫秒) 给该参数定义一个 或者Timeout Infinite可以阻止后续的定时器事件调用

  一旦构造函数被调用 你仍然可以通过Change方法改变dueTime和period

  该方法有下面四种重载形式

  public bool Change(int dueTime int period);public bool Change(uint dueTime uint period);public bool Change(long dueTime long period);public bool Change(TimeSpan dueTime TimeSpan period);

  下面是我在例子程序中用到的开始和停止该定时器的代码

  //Initialize the timer to not start automatically System Threading Timer tmrThreadingTimer = newSystem Threading Timer(new TimerCallback(tmrThreadingTimer_TimerCallback) null System Threading Timeout Infinite );//Manually start the timer tmrThreadingTimer Change( );//Manually stop the timer tmrThreadingTimer Change(Timeout Infinte Timeout Infinite);

  正如你所期望的那样 通过选择System Threading Timer类运行例子程序会产生同你看到的System Timers Timer类一样的输出结果 因为TimerCallback功能也是在工作者线程上被调用 没有一个跳动被跳过(假设有工作者线程可用) Figure 显示了例子程序的输出结果

  不像System Timers Timer类 没有与SynchronizingObject相对应的属性被提供 任何请求访问UI控件的操作都必须通过控件的Invoke或BeginInvoke方法被列集

  定时器的线程安全编程

  为了最大限度的代码重用 三种不同类型的定时器事件都调用了同样的ShowTimerEventFired方法 下面就是三个定时器事件的处理函数

  private void tmrWindowsFormsTimer_Tick(object sender System EventArgse) ShowTimerEventFired(DateTime Now GetThreadName());private void tmrTimersTimer_Elapsed(object sender System TimersElapsedEventArgse)ShowTimerEventFired(DateTime Now GetThreadName());private void tmrThreadingTimer_TimerCallback(object state) ShowTimerEventFired(DateTime Now GetThreadName());

  正如你所看到的 ShowTimerEventFired方法采用当前时间和当前线程名字作为参数 为了区别工作者线程和UI线程 在例子程序的主入口点设置CurrentThread对象的名字属性为 UIThread GetThreadName帮助函数返回Thread CurrentThread Name值或者当Thread CurrentThread IsThreadPoolThread属性为真时返回 WorkerThread

  因为System Timers Timer和System Threading Timer的定时器事件都是在工作者线程上执行的 所以在事件处理函数中的任何用户交互代码都不是马上进行的 而是被列集等候返回到UI线程上进行处理 为了这样做 我创建了一个ShowTimerEventFiredDelegate委托调用

  private delegate void ShowTimerEventFiredDelegate (DateTime eventTime string threadName);

  ShowTimerEventFiredDelegate允许ShowTimerEventFired方法在UI线程上调用它自己

  Figure 显示了发生这一切的代码

  通过查询InvokeRequired属性可以非常容易的知道你是否从当前线程可以安全的访问Windows窗体控件 在这个例子中 如果列表框的InvokeRequired属性为真 窗体的BeginInvoke方法就可以被ShowTimerEventFired方法调用 然后再被ShowTimerEventFiredDelegate方法调用 这能够保证列表框的Add方法在UI线程上执行

  正如你所看到的 当你编写异步定时器事件时有许多问题需要意识到 在使用System Timers Timer和System Threading Timer之前我推荐你阅读Ian Griffith的文章 Windows Forms:Give Your NET based Application a Fast and Responsive UI with Multiple Threads 该文刊登在MSDN杂志的 年 月份的期刊上

  处理定时器事件重入

  当和异步定时器事件打交道时 如由System Timers Timer和System Threading Timer产生的定时器事件 有另外一个细微之处你需要考虑 问题就是必须处理代码重入 如果你的定时器事件处理函数代码执行时间比你的定时器引发定时器事件的时间间隔要长 你预先又没有采取必要的措施保护防止多线程访问你的对象和变量 你就会陷入调试的困境 看一下下面的代码片断

  private int tickCounter = ;private void tmrTimersTimer_Elapsed(object sender System Timers ElapsedEventArgse)System Threading Interlocked Increment(ref tickCounter); Thread Sleep( ); MessageBox Show(tickCounter ToString());

  假设你的定时器间隔属性设置为 毫秒 你也许会奇怪当第一个信息框弹出时显示的值是 这是因为在这 秒期间第一个定时器事件正在睡眠 而定时器却在不同的工作者线程上继续产生时间消失事件 因此 在第一个定时器事件处理完成之前tickCounter变量被增加了 次 注意我使用了Interlocked Increment方法以线程安全的方式增加tickCounter变量的值 也有其它方法可以这样做 但是Interlock Increment是为这种操作而特别设计的

  解决这种问题的简单方法就是在你的事件处理函数代码块中暂时禁止定时器 接着再允许定时器 就像下面的代码

  private void tmrTimersTimer_Elapsed(object sender System Timers ElapsedEventArgse)tmrTimers Enabled = false; System Threading Interlocked Increment(ref tickCounter); Thread Sleep( ); MessageBox Show(tickCounter ToString());tmrTimersTimer Enabled = true;

  有了这段代码 消息框就会每 秒钟显示一次 就像你所期望的那样 tickCounter的值每次只增加 另外一些可选的原始同步对象就是Monitor或mutex去确保所有将来的事件被排队直到当前的事件处理函数执行完成

  结论

cha138/Article/program/net/201311/11449

相关参考

知识大全 .Net框架类库中定时器类的使用[3]

技巧:.Net框架类库中定时器类的使用[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  不像

知识大全 .Net框架类库中定时器类的使用[4]

技巧:.Net框架类库中定时器类的使用[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Sh

知识大全 .Net框架类库中定时器类的使用[2]

技巧:.Net框架类库中定时器类的使用[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  对S

知识大全 .net知识结构

  NET介绍微软NET战略及技术体系NETFramework框架类库(FCL)公共语言运行时(CLR)通用类型系统(CTS)公共语言规范(CLS)程序集(Assembly)应用程序域(Applica

知识大全 用反射调用任意.net库中的方法

注:ReturnMessage是我自写的一个类作用请查看我的其他文章我也把这个类一并附在后面通过NameSpaceAndClassName和MethodName实际上就可以精确定位一个方法了如调用ab

知识大全 从.NET类库代码来看ASP.NET运行时[1]

从.NET类库代码来看ASP.NET运行时[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&n

知识大全 从.NET类库代码来看ASP.NET运行时[2]

从.NET类库代码来看ASP.NET运行时[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&n

知识大全 ASP.NET上的ReCAPTCHA类库

ASP.NET上的ReCAPTCHA类库  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  reCA

知识大全 ADO.NET的一个帮助类库

ADO.NET的一个帮助类库  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  ADONET的一个帮

知识大全 QTP调用自己开发的.net类库

QTP调用自己开发的.net类库  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  我现在用的是QT