Multithreading 如果上一个计时仍在运行,则执行旧版VB6计时器计时堆栈或跳过

Multithreading 如果上一个计时仍在运行,则执行旧版VB6计时器计时堆栈或跳过,multithreading,vb6,timer,legacy,doevents,Multithreading,Vb6,Timer,Legacy,Doevents,我们有一个(非常)用VB6编写的遗留应用程序(15年前?) 该应用程序包含一个间隔为300ms的计时器。当计时器计时时调用的Sub执行一批代码,这些代码与一些SQL服务器对话,打印一些标签等等 当一切正常工作时,此子程序在5到10毫秒内执行,即在下一个计时器间隔出现之前执行,但在下一次计时之前也会浪费290毫秒 我们需要让这个应用程序快一点,一个选择是将间隔改为1ms-在我们这样做之前,我只想确认计时器是否会中止间隔(aka-完全忽略滴答声)如果前一个间隔仍在执行,或者它是否会开始构建对子进程的

我们有一个(非常)用VB6编写的遗留应用程序(15年前?)

该应用程序包含一个间隔为300ms的计时器。当计时器计时时调用的Sub执行一批代码,这些代码与一些SQL服务器对话,打印一些标签等等

当一切正常工作时,此子程序在5到10毫秒内执行,即在下一个计时器间隔出现之前执行,但在下一次计时之前也会浪费290毫秒

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

我已经尝试过研究这个问题,但是要找到关于旧VB6定时器的可靠信息是很困难的


我们确实安排了使用线程和后台工作线程在.net中重新写入此事件-这只是我们正在研究的短期修复。

如果计时器是GUI组件(即不是线程池计时器),并且由WM_计时器“消息”触发,则“OnTimer”事件不能“叠加”。WM_TIMER实际上并没有排队到Windows消息队列,它是在主线程返回消息队列且计时器间隔已过期时合成的

VB6计时器不是这样工作的,只有当程序空闲并停止执行代码时,才会触发Tick事件。技术术语是“再次泵送消息循环”。DoEvents泵送消息循环。这是一个非常危险的函数,因为它不仅分派计时器的滴答声事件,而且分派所有事件。包括允许用户关闭窗口或在功能仍忙于执行时再次启动功能的功能。不要使用DoEvents,除非你喜欢危险地生活或完全理解

你想让它快300倍的努力也注定要失败。首先,你不能得到一个1毫秒的计时器。Windows上的时钟分辨率不够高。默认情况下,它每秒递增64次。因此,可以得到的最小间隔是16毫秒。其次,你不能期望让慢代码任意加速,因为滴答事件不会叠加


您可以要求Windows提高时钟分辨率,它需要调用timeBeginPeriod()。这不是你应该考虑的事情。如果这真的有效,那么当你每毫秒点击一次服务器时,你肯定会得到一个非常生气的数据库管理员的访问。如果你将计时器间隔设置为快于执行时间,这个锁可能会允许你在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中顺利完成这项工作(我听说这不是不可能的)

当一切正常时,该子命令在5ms到10ms之间执行,即。 在下一个计时器间隔出现之前-但它也会浪费290ms 在下一个滴答声之前

如果时间间隔为300毫秒,这正是您设置它要执行的操作。它不是在浪费290毫秒,而是在等待300毫秒后再次触发滴答事件


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

为了最大限度地提高速度,使用一组循环指令,将计时器一起删除,并在程序入口点(Sub-Main或Form_Load)的末尾使用一个名为one的函数

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

测试它。使用间隔为1的计时器。使用计数变量。在计时器事件中,put
count=count+1:如果count=1,则:sleep(1000):如果
在1.001秒后检查计数。如果是1001,那么它们会叠加。如果是2,他们就会中止。我猜是斯塔克。使用按钮启用计时器,并使用标签显示计数。+1至Lefteris E的注释。这里有一个链接,但我不认为它能回答你的问题。我已经做了一些测试,通过向一个文件输出一个递增的var,每个文件都有一个sleep(2000)和DoEvents,即使计时器设置为1ms,它似乎也只会每隔2000年增加一次文件-DoEvents保持UI响应,因此看起来滴答子文件没有堆叠,它“看起来”就像被跳过了一样-我很想从知情人士那里得到一个正确的答案-我在VB6天里只有12岁,所以我一点线索都没有!我们知道1ms是不可能实现的:)目标仅仅是消除滴答声之间的任何空闲时间。无论如何,我们做了一些测试,并决定将其保留在300ms,改为提高SQL的性能(我在执行计划中发现了几次表扫描!)——这要容易得多,而且已经证明比以前有效得多