遗留VB6定时器如果前一个滴答仍在运行,则选中堆栈或跳过

时间:2013-04-26 10:50:55

标签: multithreading vb6 timer legacy doevents

我们有一个用VB6编写的(非常)Legacy应用程序(15岁?)。

应用程序包含一个间隔为300毫秒的计时器。当计时器滴答执行一批与某些SQL服务器通信的代码,打印一些标签等时,Sub调用。

当一切正常时,该Sub在5ms到10ms内执行 - 即在下一个定时器间隔发生之前 - 但它也会在下一个滴答之前浪费290ms。

我们需要使这个应用程序更快一点,并且一个选项是将间隔更改为1ms - 在我们这样做之前,我只想确认计时器是否会中止间隔(也就是说 - 完全忽略tick)如果前一个间隔仍然在执行 - 或者它会在一段时间后开始构建一个对sub的调用堆栈导致挂起? (我当然假设所有的滴答都在与gui相同的线程中执行 - 因此我们需要在每次滴答后使用DoEvents以确保UI不会挂起。)

我已经尝试过研究这个问题,但是找到关于旧VB6计时器的可靠信息却很棘手。

我们确实将此计划用.net使用线程和&重写。后台工作线程 - 这只是我们正在研究的短期解决方案。

5 个答案:

答案 0 :(得分:3)

这不是VB6定时器的工作原理,Tick事件只能在程序空闲并停止执行代码时触发。技术术语是“再次泵送消息循环”。 DoEvents泵送消息循环。这是一个非常危险的功能,因为它不仅会调度计时器的Tick事件,而且会调度所有事件。包括让用户关闭窗口或在忙碌执行时再次启动功能的窗口。除非您喜欢危险地或彻底地理解its consequences,否则不要使用DoEvent。

你追求快300倍的目标也注定要失败。对于初学者来说,你不能得到一个1毫秒的计时器。 Windows上的时钟分辨率不够高。默认情况下,它每秒递增64次。因此,您可以获得的最小间隔为16毫秒。其次,你不能指望任意加快代码速度,至少因为Tick事件不会叠加。

您可以要求Windows提高时钟分辨率,然后调用timeBeginPeriod()。这不是你应该考虑的事情。如果这实际上有效,那么当你每毫秒击中一台服务器时,你必然会从一个漂亮的交叉dbase管理员那里拿到一个钝器。

答案 1 :(得分:2)

如果计时器是一个GUI组件(即不是线程池计时器),并且被WM_TIMER'消息'触发,那么'OnT​​imer'事件就无法“叠加”。 WM_TIMER实际上并不排队到Windows消息队列,它是在主线程返回消息队列并且定时器间隔已过期时合成的。

答案 2 :(得分:1)

  

当一切正常时,该Sub在5ms到10ms内执行 - 即   在下一个计时器间隔发生之前 - 但它也浪费了290毫秒   在下一个刻度之前。

如果时间间隔为300毫秒,这正是您设置的内容。它不会浪费290毫秒,它会等待300毫秒,然后再次触发Tick事件。

如果您希望它更频繁地执行,则将时间间隔设置为1ms,在Tick事件开始时停止计时器,并在完成处理后再次启动它。这样,操作之间只有1ms的空闲时间。

答案 3 :(得分:0)

如果您将计时器间隔设置得比执行时间快,则此锁定可能允许您在VB6中尽快执行代码。

Private isRunning As Boolean

Private Sub Timer1_Tick()
    If Not isRunning Then
        isRunning = True
        'do stuff
        isRunning = False ' make sure this is set even in the event of an exception
    End If
End Sub

但是,如果您在此事件处理程序中尽可能多地使用,或者尽可能快地接近100%的时间,那么您的应用程序将对UI事件的响应或响应速度变慢。如果将DoEvents放在do stuff内,您将为UI提供处理事件的机会,但UI事件将暂停do stuff内的执行。想象一下移动窗口并停止执行...在这种情况下,你可能想要产生另一个线程来完成UI线程之外的工作,但是在VB6中这样做很好运(我听说这并非不可能)。

答案 4 :(得分:0)

为了最大化速度,使用循环指令集,一起删除计时器,并在程序入口点(Sub Main或Form_Load)的末尾使其具有一个名为one的函数。

在函数内,执行循环并使用QueryPerformanceCounter来管理重复间隔。这样就可以消除定时器消息系统的开销,并且可以解决定时器存在的最小定时器间隔。 在Loop的顶部添加一次Doevents,以便循环以便其他事件可以触发;等待时消耗空闲时间。