定时器参考

  1  //1.实现按用户定义的时间间隔引发事件的计时器。此计时器最宜用于Windows 窗体应用程序中,并且必须在窗口中使用。
  2
  3 System.Windows.Forms.Timer
  4
  5
  6
  7 // 2.提供以指定的时间间隔执行方法的机制。无法继承此类。
  8
  9 System.Threading.Timer
 10
 11
 12
 13 //3.在应用程序中生成定期事件。
 14
 15 System.Timers.Timer
 16
 17
 18
 19 这三个定时器位于不同的命名空间内,上面大概介绍了3个定时器的用途,其中第一个是只能在Windows窗体中使用的控件。在.NET1.1里面,第3个System.Timers.Timer,也是可以拖拽使用,而.NET2.0开始取消了,只能手动编写代码。而后2个没有限制制。下面通过具体的列子来看3个Timer的使用和区别,网上谈的很多,但基本都没有代码。
 20
 21
 22
 23 一System.Windows.Forms.Timer
 24
 25
 26
 27 #region System.Windows.Forms.Timer
 28
 29 public partial class Form1 : Form
 30
 31 {
 32
 33 public Form1()
 34
 35 {
 36
 37 InitializeComponent();
 38
 39 }
 40
 41
 42
 43 int num = 0;
 44
 45
 46
 47 private void Form_Timer_Tick(object sender, EventArgs e)
 48
 49 {
 50
 51 label1.Text = (++num).ToString();
 52
 53 Thread.Sleep(3000);
 54
 55 }
 56
 57
 58
 59 private void button1_Click(object sender, EventArgs e)
 60
 61 {
 62
 63 Form_Timer.Start();
 64
 65 }
 66
 67
 68
 69 private void button2_Click(object sender, EventArgs e)
 70
 71 {
 72
 73 Form_Timer.Stop();
 74
 75 }
 76
 77 }
 78
 79 #endregion
 80
 81 上面这个是一个很简单的功能,在Form窗体上拖了一个System.Windows.Forms.Timer控件名字为Form_Timer,在属性窗中把Enable属性设置为Ture,Interval是定时器的间隔时间。双击这个控件就可以看到Form_Timer_Tick方法。在这个方法中,我们让她不停的加一个数字并显示在窗体上,2个按钮提供了对计时器的控制功能。
 82
 83
 84
 85 执行的时候你去点击其他窗体在回来,你会发现我们的窗体失去响应了。因为我们这里使用Thread.Sleep(3000);让当前线程挂起,而UI失去相应,说明了这里执行时候采用的是单线程。也就是执行定时器的线程就是UI线程。
 86
 87
 88
 89 Timer 用于以用户定义的事件间隔触发事件。Windows 计时器是为单线程环境设计的,其中,UI 线程用于执行处理。它要求用户代码有一个可用的UI 消息泵,而且总是在同一个线程中操作,或者将调用封送到另一个线程。
 90
 91
 92
 93 在Timer内部定义的了一个Tick事件,我们前面双击这个控件时实际是增加了一行代码
 94
 95
 96
 97 this.Form_Timer.Tick += new System.EventHandler(this.Form_Timer_Tick);
 98
 99 这个应该明白,不明白的可以看我BLOG中有关委托和事件的文章。然后Windows将这个定时器与调用线程关联(UI线程)。当定时器触发时,Windows把一个定时器消息插入到线程消息队列中。调用线程执行一个消息泵提取消息,然后发送到回调方法中(这里的Form_Timer_Tick方法)。而这些都是单线程进行了,所以在执行回调方法时UI会假死。所以使用这个控件不宜执行计算受限或IO受限的代码,因为这样容易导致界面假死,而应该使用多线程调用的Timer。另外要注意的是这个控件时间精度不高,精度限定为55 毫秒。我们把Interval设置为20ms,然后在start和stop方法中记录当前时,并计算出运行时间:
100
101
102
103
104
105
106
107
108
109
110
111 从上面图可以看到程序执行了7.8S也就是7800ms,而间隔时间是20ms,也就是最后显示数字应该是390左右,但只有250,显然是不准确的,不过按MSDN说的55ms的精度,7800ms应该只执行了140多次或更少。不知道这里是不是理解有问题。
112
113
114
115 二System.Timers.Timer
116
117
118
119 接下来就看下另一个Timer,我们用他来改写上面的程序
120
121
122
123 #region System.Windows.Forms.Timer
124
125 public partial class Form1 : Form
126
127 {
128
129 public Form1()
130
131 {
132
133 InitializeComponent();
134
135 }
136
137
138
139 int num = 0;
140
141 DateTime time1 = new DateTime();
142
143 DateTime time2 = new DateTime();
144
145 //定义Timer
146
147 System.Timers.Timer Timers_Timer = new System.Timers.Timer();
148
149
150
151 private void button1_Click(object sender, EventArgs e)
152
153 {
154
155 //手动设置Timer,开始执行
156
157 Timers_Timer.Interval = 20;
158
159 Timers_Timer.Enabled = true;
160
161 Timers_Timer.Elapsed += new System.Timers.ElapsedEventHandler(Timers_Timer_Elapsed);
162
163 time1 = DateTime.Now;
164
165 }
166
167
168
169 void Timers_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
170
171 {
172
173 label1.Text = Convert.ToString((++num)); //显示到lable
174
175 Thread.Sleep(3000);
176
177 }
178
179
180
181 private void button2_Click(object sender, EventArgs e)
182
183 {
184
185 //停止执行
186
187 Timers_Timer.Enabled = false;
188
189 time2 = DateTime.Now;
190
191 MessageBox.Show(Convert.ToString(time2-time1));
192
193 }
194
195 }
196
197 #endregion
198
199 我们可以看到这个代码和前面使用Form.Timer的基本相同,不同的是我们是手动定义的对象,而不是通过拉控件。他也有Interval ,Enabled 等属性,作用和第一是一样的。不同的是他的事件名为Elapsed ,但是和上面的Tick一样,绑定一个委托的方法。只是这里我们是手动完成的。另外不同之处是Form.Timer我们可以用Stop和Start方法控制,而这里是通过Enable属性控制。但实际上也可以用Stop和Start方法,内部也是通过他自己的Enable来控制的。
200
201
202
203 最大的不同就是上面的代码在调试时会报错,提示你"线程间操作无效: 从不是创建控件“label1”的线程访问它。"但如果你不调试直接运行是OK的,而且运行时你去拖动窗体会发现没有出现假死。从这里我们就可以知道这里的Timer的创建线程和执行线程不是同一个线程。也就是使用了多线程。Timer的创建线程是UI线程,而执行线程是TheardPool中的线程,所以不会假死,但调试的时候会报错,因为非控件的创建线程不能操作控件。但你可以直接运行,这里是VS05做了手脚。解决办法很多,用delegate.BeginInvoke()等等。这里介绍特有的一种方法,设置Timer的SynchronizingObject属性,Timers_Timer.SynchronizingObject = label1;这样的话,我们的话,调试运行时就不会报错了,但是设置了这个属性Timer就编程单线程调用了,就基本和第一个完全一样了。
204
205
206
207 Timer 是为在多线程环境中用于辅助线程而设计的。服务器计时器可以在线程间移动来处理引发的Elapsed 事件,这样就可以比Windows 计时器更精确地按时引发事件。Elapsed 事件在ThreadPool 线程上引发。如果Elapsed 事件的处理时间比Interval 长,在另一个ThreadPool 线程上将会再次引发此事件。因此,事件处理程序应当是可重入的。
208
209
210
211 另外和前面不同的现象是每次加1后并没有停止3秒在显示。而是继续显示,只是速度稍慢。因为我们设置间隔为20ms,而执行时间为3s,所以会在20ms后在另一个线程中继续执行,而当前线程被挂起而已。关于计时器的精度,取消3s的挂起,发现结果和第一个基本一致。
212
213
214
215 三System.Threading.Timer
216
217
218
219 继续用这个对象改造程序。
220
221
222
223 #region System.Windows.Forms.Timer
224
225 public partial class Form1 : Form
226
227 {
228
229 public Form1()
230
231 {
232
233 InitializeComponent();
234
235 }
236
237
238
239 int num = 0;
240
241 DateTime time1 = new DateTime();
242
243 DateTime time2 = new DateTime();
244
245 System.Threading.Timer Thread_Time;
246
247
248
249 private void button1_Click(object sender, EventArgs e)
250
251 {
252
253 //启动
254
255 Thread_Time = new System.Threading.Timer(Thread_Timer_Method,null,0,20);
256
257 time1 = DateTime.Now;
258
259
260
261 }
262
263
264
265 void Thread_Timer_Method(object o)
266
267 {
268
269 label1.Text = Convert.ToString((++num));
270
271 System.Threading.Thread.Sleep(3000);
272
273 }
274
275
276
277 private void button2_Click(object sender, EventArgs e)
278
279 {
280
281 //停止
282
283 Thread_Time.Dispose();
284
285 time2 = DateTime.Now;
286
287 MessageBox.Show(Convert.ToString(time2-time1));
288
289 }
290
291 }
292
293 #endregion
294
295 用Threading.Timer时的方法,和前面就不太相同了,所以的参数全部在构造函数中进行了设置,而且可以设置启动时间。而且没有提供start和stop方法来控制计时器。而且是以一种回调方法的方式实现,而不是通过事件来实现的。他们之间还是有区别的。
296
297
298
299 我们只有销毁掉对象来停止他。当你运行时,你会发现他和前面的Timers.Timer一样,是多线程的,主要表现在不会假死,调试运行报错。但跟让你奇怪的是,我们的代码竟然无法让她停止下来。调用了Dispose方法没有用。问题在那?然后有进行了测试,修改了间隔时间为100,200,500,1000,3000,4000。这几种情况。发现当间隔为500ms以上是基本马上就停止了。而间隔时间相对执行时间越短,继续执行的时间越长。这应该是在间隔时间小于执行时间时多个线程运行造成的。因为所有的线程不是同时停止的。间隔越短,线程越多,所以执行次数越多。
300
301
302
303
304
305
306
307 最后来看下这个对象另外一个特殊的地方。
308
309
310
311 static void Main()
312
313 {
314
315 Timer t = new Timer(Test,null,0,1000);
316
317 Console.ReadLine();
318
319 }
320
321
322
323 public static void Test(object o)
324
325 {
326
327 Console.WriteLine("nihao");
328
329 GC.Collect();
330
331 }
332
333 这段代码会输出什么结果呢?默认情况他只输出一次,就停止了。为什么呢?根据上面说的,当定义对象t,执行代码后,进行了强制垃圾回收,因为t在Main中没有其他引用,所以被回收掉了。但是如果我们吧编译器的”优化“项取消掉,在看看情况。程序进然一直在输出。为什么执行垃圾回收却没有被回收呢?因为这个禁用优化选项,t的声明周期被扩展到了方法结束。所以一直执行。
334
335
336
337 因为编译器默认是优化的,所以我们必须保证Timer对象一直被引用,而避免被垃圾回收。所以我们可以在编译器打开优化的情况下,在Main函数最后加上t=null保证回收前被引用,但你发现,这样是没用的。因为JIT编译器优化后会吧t=null直接删除,所以我们用t.Dispose(),就可以达到目的。在我们进行垃圾回收时,CLR发现t还有被引用,还没执行Dispose所以不会被回收。是以Threading.Timer有时候会出现运行一次就停止或者是销毁了还在运行的情况,而且和编译器优化也有关,所以使用时要注意。
338 最后看下MSDN的描述: 只要在使用Timer,就必须保留对它的引用。对于任何托管对象,如果没有对Timer 的引用,计时器会被垃圾回收。即使Timer 仍处在活动状态,也会被回收。当不再需要计时器时,请使用Dispose 方法释放计时器持有的资源。如果希望在计时器被释放时接收到信号,请使用接受WaitHandle 的Dispose(WaitHandle) 方法重载。计时器已被释放后,WaitHandle 便终止。

System.Threading.Timer 是一个简单的轻量计时器,它使用回调方法并由线程池线程提供服务。不建议将其用于Windows 窗体,因为其回调不在用户界面线程上进行。System.Windows.Forms.Timer 是用于Windows 窗体的更佳选择。要获取基于服务器的计时器功能,可以考虑使用System.Timers.Timer,它可以引发事件并具有其他功能。

在《CLR Via C#》中讲多线程时有提到这3个计时器,但作者说System.Timers.Timer是对System.Threading.Timer的报装,不推荐使用,但是在我的WEB项目中的Application_Start中我还是使用的这个而不是Threading.Timer,因为使用Threading.Timer时只执行了一次就不在执行了。

对于计时器在B/S结构中的使用就复杂一些,一般我们把计时器放在Application_OnStart中,这样全局维护一个计时器,可以进行定期备份数据库,定期维护用户等操作,而且方法写作静态的,以免被垃圾回收。而不建议在一般的aspx页面中使用,因为服务器端的定时器对用户这样意义不大,完全可以使用JS代替。而且这个页面的每个请求都可能引入一个新的定时器,导致系统崩溃。另外,定时器是ASP.NET进程,IIS有关,所以对用重要的执行任务,还是建议写成服务或独立程序放在服务器上执行好了

时间: 2024-07-30 04:57:24

定时器参考的相关文章

高性能javascript学习笔记系列(5) -快速响应的用户界面

参考高性能javascript 理解浏览器UI线程  用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程  UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 <button onclick="handleClick()">click</button> <script type="text/javascript"> function handleClick() { var div = do

usosiii时钟节拍

在APP组里的os_cfg_app.h文件可以设置系统时钟SysTick的时钟节拍频率(还可以设置它的任务优先级,任务栈大小,时间片数组大小),一般使用1000Hz,也就是节拍周期为1ms. #define OS_CFG_TICK_RATE_HZ 1000u /* Tick rate in Hertz (10 to 1000 Hz) */ #define OS_CFG_TICK_TASK_PRIO 10u /* Priority */ #define OS_CFG_TICK_TASK_STK_S

定时器的实现原理及参考

如果让你来实现一个定时器的功能,简单点就是,每隔n秒,去执行一次A任务,你打算怎么实现? 我觉得一般都能想到,使用一个死循环,然后每次循环比较时间,时间到了就去执行A任务就好了.但是这样会不会有问题?每次循环会不会性能消耗太大?别人都是怎么做的?如果有语言提供的工具,那我自然更加相信他而不是自己去实现. 好吧,用编程语言自身提供的工具一般情况下自然是比较明智的选择,因为别人本来就比你厉害啊. 那么,java中的定时器?不用说,timer.是怎么做的呢?他到底比自己好在哪里,他肯定是用了什么我不知

定时器(Quartz)快速入门

Quartz概述 Quartz中的触发器 Quartz中提供了两种触发器,分别是CronTrigger和SimpleTrigger. SimpleTrigger 每 隔若干毫秒来触发纳入进度的任务.因此,对于夏令时来说,根本不需要做任何特殊的处理来"保持进度".它只是简单地保持每隔若干毫秒来触发一次,无论你的 SimpleTrigger每隔10秒触发一次还是每隔15分钟触发一次,还是每隔24小时触发一次. CronTrigger 在特定"格林日历"时刻触发纳入进程的

STM32学习笔记(五)——通用定时器计数延时

STM32定时器概述 STM32F40x系列总共最多有14个定时器,定时器分为三类:基本定时器.通用定时器和高级定时器.它们的都是通过计数来达到定时的目的,和51的定时器差不多,基本原理都是一样的,就是功能多了一些,这些计数器都是自动重新装载初值的,使用起来非常方便,而且计数时钟频率可以通过分频系数来设置.本文章将介绍使用定时器中断来控制LED间隔1s闪烁. 计数的时钟来源主要有四个: 内部时钟CK_INT 外部时钟模式1:外部输入脚TIx 外部时钟模式2:外部触发输入ETR,仅适用于 TIM2

4-MSP430定时器_1

自己学MSP430是为了写一篇关于PID的文章,需要430在proteus上做仿真,一则认为在自动控制算法上PID真的很经典,PLC设备上大多是模块式的,拿来就是参考说明书设置,设置,,,而对于单片机而言就是程序!!!只有自己写出来PID算法,才能对PID有更深刻的理解,..二来感觉自己已经好久都没有写一篇让自己感到满意的文章了...... 由于定时器A需要选择时钟来源,,所以先了解430F249的时钟系统 通俗来讲,430F249有三个振荡器,LFXT1,,XT2,,DCO, 内部的主时钟MC

IOS开发定时器延时的探究

在日常开发中,我们经常要延时等待(如网络请求,UI更新完)然后做一些事情,或者是做一些周期性的处理.这个时候就要求我们实现一个周期性的定时器进行延时操作.常用的延时实现方法有下面三种: 1.NStimer实现 不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时触发.重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周

关于JavaScript定时器我的一些小理解

因为自己在平时工作中,有些功能需要用到定时器,但是定时器并不像我们表边上看到的那样,所以这周末我看看书查查资料,深入研究了一下JavaScript中的定时器,那么废话不多说,下面进入我们今天的正题. 大家都知道JavaScript是单线程的,所以不管是定时器还是用户的操作都是需要在线程队列中排队执行的. 一.定时器在执行线程队列里的分析 为了更好的理解我还是直接写个测试代码来看一下,这样分析起来更直观一些 1 <script type="text/javascript">

Qt on Android: Qt Quick事件处理之鼠标、键盘、定时器

在<Qt on Android: Qt Quick 事件处理之信号与槽>中介绍了 QML 中如何使用内建类型的信号以及如何自定义信号,这次我们来看看如何处理鼠标.键盘.定时器等事件.这些时间在处理时,通常是通过信号来完成的. 广而告之:我正在参加 CSDN 博文大赛,请给我的参赛文章<Qt on Android: Qt Quick 事件处理之信号与槽>投票,谢谢. 鼠标事件处理 桌面开发的话,难免要处理鼠标事件-- 变色矩形示例 看一个简单的处理鼠标事件的例子,先看代码(handl