《iFix定时器控件使用心得》
(关键字:定时器,时钟,计时器,假死,Timer,Interval)
   iFix的定时器控件,经常会把初学者搞得头晕脑涨,我说说自己的心得,供大家参考。
   该控件是基于调度功能的,所以有一些用法和其它编程平台(如VB)里的定时器不太一样。
  一、定时器的启动/停止
timeout on t2 timer  如果在编辑时TimerEnabled设为True,则运行时定时器会自动启动。如果在编辑时TimerEnabled设为False,则运行时需要先将TimerEnabled设为True,然后调用StartTimer方法。如果只将TimerEnabled设为True,但是不调用StartTimer方法,定时器是不会开始工作的。如果要停止定时器,可以调用StopTimer方法,也可以直接将TimerEnabled设为False。也就是说有两种控制定时器启/停的方式:
方式一:
用如下代码启动:Timer1. TimerEnabled=True
Timer1. StartTimer
用如下代码停止:Timer1. TimerEnabled=False
方式2:
在画面或调度的Initializes事件中加入:Timer1. TimerEnabled=True
用如下代码启动:Timer1. StartTimer
用如下代码停止:Timer1. StopTimer
  二、以“连续”方式使用
  以“连续”方式使用时,你会发现这样的现象,例如你希望一个画面被打开10秒钟后自动关闭,你会将定时器的Interval属性设为10000,事实上却是,画面不到10秒就会被关闭,而且每次的延时时间还不是固定的,似乎是随机的,有时几乎是10秒,有时还不到1秒。这个举例中,定时器仅运行了1次(因为画面已经被关闭了),如果定时器一直运行下去,你还会发现,除了第一次的延时是“随机”的,从第二次开始,延时都是准确的。
  这究竟是怎么回事呢?其实这是StartTime属性在起作用,StartTime属性的默认值是0:00:00,表示从午夜0点0分0秒开始,在这种情况下,如果设置为10秒钟的间隔,定时器被触发的时间将是每一分钟
的0秒、10秒、20秒……50秒,如果从某一分钟的18秒启动了定时器,那么定时器第一次被触发的时间将会是20秒,也就是说,从启动到第一次触发之间仅有2秒钟的延时,如果你从15秒启动,会得到5秒钟的延时,这就是为什么你会觉得第一次的延时是“随机”的。
  好的,既然知道了原因,自然也就有了解决的方法,那就是在每一次调用StartTimer方法之前,将StartTime属性设为当前时间,即Timer1. StartTime = Now就搞定了。例如在8:15:23秒启动,间隔10秒,第一次触发将会是在8:15:33秒的时候。
  那是不是所有以“连续”方式使用时,这样作就都OK了呢?不是。这个方法是否有效(也就是得到精确的延时),要看你所希望的延时时间有多长,如果延时是10秒或更长,那没问题,这样是唯一正
确且简便的方法。但如果你设置的延时间隔比较小,如3秒以内,甚至是毫秒级的,那么这个方法就会产生比较严重的误差。因为StartTime属性的时间精度只达到秒级,也就是说,实际的运行效果还是会有一定的误差,当然,这个误差最大不会超过1秒,所以一般来说对于5秒以上的延时设置,这个误差可以忽略。但如果延时设置是2秒,然后产生了接近1秒的误差,这就成问题了,误差率将近50%啊!如何解决此类问题呢?有办法,只是稍复杂一点。
  例如,我们希望做到这样一个效果——有一个按钮对象(名为cmd1),当用鼠标点击这个按钮时,按钮消失不见,2秒钟之后又出现。也就是说在点击的时候把按钮的Visible属性设为False,并且启动
一个定时器控件,2秒之后在定时器的OnTimeOut事件代码中,再把cmd1的Visible属性设为True。
  如何比较精确地实现这2秒的时间间隔呢?具体做法是:将定时器的Interval属性设为100毫秒,定义一个模块级变量intTimes,定时器每触发1次,这个变量的值就增加1,定时器被触发20次后,变量的值为20,总时间正好是2秒。代码如下:
  Private Sub Timer1_OnTimeOut(ByVal lTimerId As Long)
   If intTimes < 20 Then
      intTimes = intTimes + 1
      Exit Sub
   End If
   Timer1.StopTimer
   intTimes = 0
   cmd1.Visible = True
  End Sub
  这样,就很好的解决了时间精度的问题(误差在100毫秒以内)。但是这样做还有一点小的瑕疵,尽管变量intTimes只有一个地方使用,依然必须在通用区进行定义,代码的内敛性太差,移植不方便。但这个问题也可以解决。定时器控件有一些特殊的属性:Property1~ Property10,这些属性你可以用来存放任何值。我们就要利用这些属性,一个就够,我们就选Property1。首先,在编辑时将Property1 设为0,然后在OnTimeOut事件中加入如下代码:
  Private Sub Timer1_OnTimeOut(ByVal lTimerId As Long)
   If Timer1.Property1 < 20 Then
      Timer1.Property1 = Timer1.Property1 + 1
      Exit Sub
   End If
   Timer1.StopTimer
   Timer1.Property1 = 0
   cmd1.Visible = True
  End Sub
  三、堵塞问题
  就是在连续运行方式下, OnTimeOut事件中的代码中,一定要小心使用循环结构,需要循环的话,尽量使用For循环。如果必须要使用Do…Loop,首先要绝对避免任何死循环的可能,即使不会发生死循环,也一定要保证循环次数是可以预见的并且总循环时间一定要小于定时器的间隔时间,比如有个朋友发生过这样的问题,他在一个画面中使用了一个COMM控件,用于和设备进行串口通讯,他在一个定时器中编写脚本,运行时,
有时会频繁的出现假死状态,我在电话中帮他解决问题,经过反复询问,最后发现罪魁祸首就是循环。他在发出一个读取指令后,用一个Do…Loop不停的检测是否返回了所需要的数据,如果有则结束循环,但有些时候通讯状态不是很好,数据返回比较慢,甚至是严重超时,结果循环就无限期的进行下去,CPU被占满,系统假死,他改用别的方法后,问题解决。
如果时间间隔设置的比较短,即使代码中没有循环结构,有些操作也会导致运行时间超过定时器的间隔时间,如果这个超时是可接受的正常情况,那么就要注意另一个问题,那就是定时器的QueueEvent
s属性。它决定了当定时器某一次触发的脚本还没有执行完毕,下一次触发时间就到时了,这种性况下,新的事件是排队等候,还是被丢弃掉。默认值是False,如果希望它们“一个都不能少”,那么就要把这个属性改为True。但你必须保证这种超时只是偶然发生,否则,这个队就会也排越长,直到崩溃。
  四、挂起问题
  还有一个要注意的问题,如果你在前台(工作台)画面中使用了连续方式的定时器,那么你一定要知道,当你显示了一个有模式的窗体或者使用MsgBox函数时,工作台中的任何脚本都会被挂起(也就是停止运行),同样,定时器也不会再继续触发,直到窗体或消息框被关闭。比如你像前面提到的那个老兄,在画面利用定时器进行通讯,那么,当显示有模式窗体或消息框时,通讯工作就会被停止,直到窗体或消息框被关闭。所以如果需要通讯的话,最好编写一个独立的EXE程序比较好。再比如,你要每小时整点的时候进行一次抄表工作,定时器的时间间隔为1小时。那么如果8点整的时候,你已经有一个消息框显示后未被关闭,这次抄表就会缺失。此类工作应该在后台调度中进行。
最后再提一个小事,默认情况下间隔时间的单位是毫秒,3秒钟就写作3000,但如果3小时怎么写呢?10800000?太夸张了。其实可以用“时:分:秒”的格式,3小时写作3:00:00,超过1秒的时间间隔都可以用这种格式。但是只能在属性窗口中这样写,新建定时器的对话框中不行。
  以上是我在工作中使用定时器的一些心得,仓促写下,如果有错漏之处请告诉我,我尽快修正,以免误人。定时器的其他用法,由于我使用的较少,就不乱说了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。