【问题标题】:Can a Timer event experience reentrancy?Timer 事件可以重入吗?
【发布时间】:2015-06-02 19:32:21
【问题描述】:

我正在检查一些可怕的遗留代码,这些代码具有 Timer 事件以及一些包含 DoEvents 调用的冗长代码。简化版如下所示:

   Private Sub tmrProcess_Timer()
      'Run some slow processing code here
      DoEvents
      'More slow code here
      DoEvents
      'Lots more slow code and the occasional DoEvents here
      If booComplete Then
         tmrProcess.Enabled = False
      End If
   End Sub

计时器的间隔设置为 250,慢代码可能需要 30 秒左右才能完成。请注意,表单上有一个按钮在单击时设置 booComplete = True。

鉴于 VB6 是单线程的并且计时器消息的优先级较低,是否完全有可能在 DoEvents 调用期间重新输入 Timer 事件,或者如果 Timer 事件发生,VB6 运行时会阻止 Timer 事件的执行正在执行?

This reference 有一些相关信息。特别是它指出 WM_PAINT 消息被组合成一条消息,但没有提及 WM_TIMER 消息是否被组合。

【问题讨论】:

  • 我为这个问题写了一个小测试程序。 (定时器函数内的长时间运行循环,进入定时器事件时增加一个静态计数器,离开时递减,但在长时间运行循环后输出计数器的值。)似乎VB6没有重新进入定时器例程。
  • 如果您有一个运行时间超过计时器间隔的方法,则间隔事件将排队,并且如果您的方法中有任何内容(DoEvents)允许触发事件,您可以在您之前再次输入计时器事件方法已退出。
  • @jac : 您能否将您的测试项目作为答案发布.. 请在下面查看我的答案,其中包含一个不会重新进入计时器事件的测试项目
  • @bob77 我不是在要求最佳实践或如何重构,这是其他人编写的可怕的遗留代码,我需要了解它是如何工作的 - 或者而是如何失败
  • @Hrqls 我已经删除了我之前的示例,所以我尝试重新创建它,现在它没有重新输入,所以......我的错?

标签: timer vb6 message-queue


【解决方案1】:

我预计它会重新进入,但似乎不会

看看下面的测试项目:

'1 form with:
'  1 timer control : Name=Timer1

Option Explicit

Private Sub Form_Load()
  WindowState = vbMaximized
  Timer1.Interval = 2000
End Sub

Private Sub Timer1_Timer()
  Static intCount As Integer
  Dim sngTime As Single
  intCount = intCount + 1
  Print CStr(Now) & " Timer event fired " & CStr(intCount)
  sngTime = Timer + 3
  Do While sngTime > Timer
    DoEvents
  Loop
  Print CStr(Now) & " End of timer event " & CStr(intCount)
End Sub
  • 您将在表单加载 2 秒后看到“开始”
  • 您将在 3 秒后看到“结束”
  • 您将在前一个“结束”显示 2 秒后看到一个“开始”
  • 您将在“开始”后 3 秒看到“结束”
  • ...

如果重新输入计时器,我预计每次“开始”之间有 2 秒,但每次“开始”之间似乎有 3+2=5 秒

删除 DoEvents 不会改变行为,它只会改变文本打印的时间

【讨论】:

  • 知道为什么吗?是 VB6 运行时的实现还是取决于 Windows 处理计时器消息的方式?
  • @ClaraOnager: WM_TIMER 是可重入的。 VB6 定时器不会被MsgBox 函数明确阻止。
  • @wqw 这意味着在 VB6 运行时中执行 Windows 过程可以防止重入
  • @ClaraOnager:那个,或者它正在使用线程计时器并且与WM_TIMER无关。
  • 我同意这一点。在我的应用程序中,如果计时器处理程序花费的时间超过计时器间隔,则不会重新输入。处理程序的下一次调用是在函数退出后的完整间隔时间之后再次调用。因此,它的行为就好像在处理程序运行时计时器暂停了一样。 DoEvents 不影响它,它允许 /other/ 定时器运行,但不是同一个。
【解决方案2】:

为避免重新进入,请禁用计时器,直到执行计时逻辑,然后再次启用。

Private Sub tmrProcess_Timer()
  tmrProcess.Enabled = False

  'your time taking logic goes here ......

  tmrProcess.Enabled = True
End Sub

【讨论】:

  • 虽然这是一个类似问题的解决方案,但它不是这个问题的实际答案
  • 确实如此。 rags 应该编辑他的答案以添加字符串“是”。那么这将是问题的实际答案......
  • 我假设@ClaraOnager 希望定时器事件定期触发......如果你禁用定时器,然后再次启用它,那么间隔是常规的+不必定期...例如她希望计时器每 10 分钟触发一次,但逻辑需要 30 秒,然后计时器将每 40 秒触发一次,而不是每 10 秒触发一次
  • 有点,记住这是遗留代码,我需要了解它是如何工作的,所以我不重构它,它的功能如问题中所述 - 250ms 间隔和长达 30 秒的操作
【解决方案3】:

首先,大多数包含 DoEvents 的代码都不需要它,它是一个神奇的词,人们感到不得不咒语(但不知道为什么)。

DoEvents 允许重入任何东西,而不仅仅是计时器。

你的是一个 TimerProc。如果您选择了一条消息,那么 wm_timer 消息仅在消息队列为空并且您询问“他们有消息吗?”时才会出现。如果它们是绘画、计时器或鼠标移动类型的消息待处理,那么只有当队列为空时它们才可用。


尽管很明显,这是它来自的链接:

> mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20Studio\MSDN\2001OCT\1033\kbvb.chm::/Source/vbapps/q118468.htm

(当然,您必须安装与我相同的库才能正常工作。)

为什么你认为来源是互联网?

Visual Basic for Applications 中 DoEvents 的定义

Q118468


本文中的信息适用于:

Microsoft Visual Basic for Applications 1.0 版 Microsoft Excel 对于 Windows,版本 5.0、5.0c 用于 Macintosh 的 Microsoft Excel, 5.0、5.0a 版 Microsoft Excel for Windows 95,7.0、7.0a 版 Microsoft Excel 97 for Windows Microsoft Excel 98 Macintosh 版


摘要 DoEvents 函数放弃了宏的执行,因此 操作系统可以处理其他事件。 DoEvents 函数将控制权从应用程序传递给操作系统。 DoEvents 可能有用的一些实例包括:

硬件 I/O

延迟循环

操作系统调用

DDE 死锁

本文还讨论了与 DoEvents 函数。

更多信息

硬件 I/O 如果您的代码等待来自任何 I/O 设备的输入,则 DoEvents 函数通过多任务处理加速应用程序。作为一个 结果,计算机似乎没有暂停或停止响应(挂起) 在代码执行时。

例子:

打开“com1”作为输入 #1 输入 #1,x 执行直到 x = Chr(13) DoEvents '... '... 输入 #1,x 循环延迟循环 在延迟循环中,DoEvents 功能可以让CPU操作系统继续任何 待处理的作业。

例子:

X = Timer() X + 10 > Timer()

DoEventsLoop 操作系统调用当 Visual Basic 调用 操作系统,操作系统甚至可以返回控制权 在完全处理命令之前。这样做可能会阻止任何 依赖于由调用生成的对象的宏代码 跑步。在下面的示例中,Shell 函数启动 Microsoft 词应用。如果 Word 还没有打开,任何努力建立一个 指向它的 DDE 链接将停止代码。通过使用 DoEvents,您的程序 确保一个操作,如 Shell,被完全执行 在处理下一个宏语句之前。

例子:

z% = Shell("WinWord Source.Doc",1) DoEvents ... ... DDE 死锁 考虑一个 Visual Basic 宏调用 正在等待第二个应用程序获取一些数据的应用程序。 如果宏没有将控制权交给第二个应用程序,则 结果是死锁。在多个 DDE 对话中 应用程序,使用 DoEvents 消除了这种类型的可能性 僵局。与使用过多嵌套的 DoEvents 相关的问题 DoEvents 语句可能会耗尽堆栈空间并因此生成 “堆栈空间不足”错误消息。这个错误是指 分配给 Microsoft Excel 应用程序的应用程序堆栈空间。

确保通过 DoEvents 放弃控制的过程不是 在第一次之前从代码的不同部分再次执行 DoEvents 调用返回;这可能会导致不可预知的结果。

一旦 DoEvents 将控制权交给操作系统,它就不会 可以确定 Microsoft Excel 何时恢复控制。 操作系统获得处理器的控制权后, 处理当前在消息队列中的所有未决事件 (例如鼠标点击和击键)。这可能不适合某些人 实时数据采集应用程序。

参考 有关 DoEvents 的更多信息,请单击搜索 帮助中的按钮并输入:

事件

附加查询词:Sendkeys keystroke 等 XL98 XL97 XL7 XL5

关键字:问题类型:技术:kbHWMAC kbOSMAC kbExcelSearch kbZNotKeyword6 kbExcel95 kbExcel500 kbExcel98 kbExcel95Search kbExcel97Search kbExcel98Search kbExcelMacsearch kbVBASearch kbZNotKeyword3 kbExcel500Mac kbExcel500aMac kbExcel500c kbExcel95a kbVBA100

上次审核时间:2001 年 1 月 17 日 © 2001 Microsoft Corporation。全部 保留权利。使用条款。


向 MSDN 发送反馈。在此处查找 MSDN Online 资源

【讨论】:

  • 'DoEvents 允许重入任何东西,而不仅仅是计时器' - 这是一个非常大胆的声明,你确定它是正确的吗?
  • VB6 帮助文件。警告 任何时候在事件过​​程中临时让出处理器时,请确保在第一次调用返回之前不会从代码的不同部分再次执行该过程;这可能会导致不可预测的结果。此外,如果在您让出控制权期间其他应用程序可能以不可预见的方式与您的过程交互,请不要使用 DoEvents。
  • 只有一个问题,实际实验表明Timer事件不会重入。你有显示重入的测试程序吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
  • 2012-11-05
  • 1970-01-01
  • 2011-03-19
  • 2023-04-04
  • 2010-10-12
  • 2011-03-08
相关资源
最近更新 更多