繁体   English   中英

Excel VBA 宏速度不可预测地变慢,通常在 DoEvents

[英]Excel VBA macro slows down unpredictably, usually at DoEvents

是什么导致 Excel 变得像爬行一样缓慢,有时是一两分钟,有时是几个小时,而且它的工作负载分散在对DoEvents的调用中?

不幸的是,我无法发布代码,因为似乎没有导致问题的代码。 它发生在不同的地方,没有明显的模式。 下面是我的调查的详细信息。

上周 Excel 在我的电脑上运行一个已经运行多年没有问题的宏时开始变慢。 我在另外 2 台计算机上测试了相同的宏,但它们似乎没有相同的问题。 很难测试,因为它并不总是减慢速度,也不会在同一个地方发生。 有时它每分钟执行一次,有时它会工作 10 分钟,然后再执行一次。 我尝试了另外两台计算机大约 30 分钟,但从未发生过。

如果我设置断点,它永远不会减速。

如果我喷洒印花,它会喷洒,但绝不会喷洒在同一个地方。

如果我按 Ctrl+Break 宏会在几分钟后停止,调试器会像往常一样突出显示当前行,但 Excel 会继续使用 25% 的 CPU(即两个内核的 100%)。 此时每次点击 Excel 或 VBA IDE 都有响应,但非常慢。 您通常需要几分钟才能看到 cursor 移动到单击的位置。 有时,如果您等待几分钟,控件会返回,并且可以按 F8 或 F5 继续执行,但如果 Excel CPU 使用率在 2-3 分钟内没有下降,我通常会终止 go。

在调查期间,我创建了这个 function:

Sub DoEvents2()
  Dim T As Single
  T = Timer
  DoEvents
  Debug.Print Format(Timer - T, "0.000")
End Sub

并将对DoEvents的调用替换为对DoEvents的调用,我在调试 window 上看到DoEvents2所需的时间始终是几千秒,有时是几百秒,偶尔,没有明显的模式,时间往上。 通常它会上升到 90 秒左右,有时会更少,有几次它持续了将近一个小时,有一次它在第二天仍在运行。

这是在使用上面定义的DoEvents2运行宏后调试 window 上的 output 的示例:

0.000
0.289
0.000
0.004
0.066
88.324
26.727
20.699
28.762
4.359
0.789
0.090
0.297
0.141
0.000
0.070
0.000
0.043
[...]
0.016
0.035
0.004
0.199
1.852
0.066
0.023
0.004
0.000
31.309
104.438
1.449
0.785
0.020
0.004
0.547
0.000
0.000
0.055

宏很大,很难在不破坏它的情况下移除一块。

起初我以为问题出在表格上,但我删除了所有 forms 问题仍然存在。

然后我尝试处理 volatile 函数:我删除了所有 volatile 函数,但问题仍然存在。 另外,它似乎不是由Application.CalculationModeApplication.EnableEvents的更改触发的。

我检查了是否有全局变量可以触发长时间运行的垃圾收集,但我没有找到任何东西。 另外,我不明白垃圾收集怎么能运行 12 个小时(我让它整晚运行一次,早上它还在那里)。

有时我点击一个运行宏的按钮,该宏使用JsonConverter 、正则表达式、http 请求、forms、volatile 函数等,它按预期运行一两分钟,没有问题。 然后我点击另一个运行 10 行代码的按钮,它在几百秒内完成。 我一次又一次地单击同一个按钮,几次后 Excel 挂起。 我不知道它挂在哪里,因为它从不挂在同一条线上。

问题是上周Windows更新时出现的,不知道有没有关系。 从那时起,我一直在全职解决这个问题,但没有成功。

我不能发布任何代码,因为宏有 14,000 行,它发生在任何地方,只要有DoEvents ,即使它是一个小的 5 行 function。但它似乎受到更早发生的事情的影响。

所以,我的问题是,会发生什么事情会减慢 Excel 的速度,有时会持续一两分钟,有时会持续数小时,而且它的工作负载会分散到对DoEvents的调用中。

编辑

大约 40 分钟前它又发生了,我决定让它发泄,希望它能在我做其他事情时停止。 30 分钟后,我按 Ctrl+Break 试图停止它,一两分钟后它确实停止了,然后我对 VBA IDE 的保存按钮进行了一阵点击,希望其中一个可以工作,一分钟后或两个弹出窗口显示询问我是否要保存未完成计算的文件。 几秒钟后,虽然可以看到此弹出窗口,但 CPU 使用率变为零。 我要求在不完成计算的情况下保存文件,它保存了(我可以在标题栏上看到“-已保存”),然后 CPU 继续吸取 2 个内核,IDE 仍然显示当前行突出显示。 我尝试在停止按钮上点击一下,但没有用(还没有?)。

编辑 2

我在宏中找到了一个位置,如果我设置一个断点并按 F5,它会始终如一地工作,但如果我删除断点它会工作一次,然后它会挂起。

这是代码:

T0 = Timer
Debug.Print Application.CalculationState,
DoEvents ' Here I set the breakpoint
Debug.Print Application.CalculationState, Timer - T0

这是立即 window 上的 output 在运行几次断点后(你看到我在看到它停止后按 F5 所需的时间)和两次没有断点。 我正在粘贴快照,因为我无法从挂起的 Excel 准备被杀死。 在执行该代码时,计算是手动的,并且事件被禁用。 我不认为这是在计算。

在此处输入图像描述

明天我将尝试重新安装 Office。

编辑 3

卸载并重新安装 Office 没有帮助。

但我想我发现了一个似乎可以可靠地允许 Excel 正常工作或挂起的因素:VPN。

如果我在没有 VPN 连接的情况下启动 Excel,那么我可以毫无问题地工作。 如果我然后启动 VPN 连接,第一次点击(和下面的宏)没有问题,第二次点击很慢,第三次挂起,Excel 需要被杀死。 第二次点击后断开 VPN 连接无济于事。 我对这种情况进行了 5-6 次测试,结果始终相同。

我想不出 Excel 会受到 VPN 连接影响的任何原因。 唯一安装的插件是我正在努力使用的插件,它们在本地驱动器上,没有安装第 3 方插件。 我的宏不访问任何网络驱动器。 有一个 class 和一个成员Req As New MSXML2.XMLHTTP60在我的测试中从未使用过。

编辑 4

不,与 VPN 无关。 今天我在办公室,没有VPN,问题依然存在。

重新安装 Office 后,我的设置与以前相同。

是否有可能重置所有内容并制作一个新的干净的 Office 安装,不记得以前生活中的任何东西?

这是一个小更新。 我不知道这是否是可以解决问题的答案,但这是我在问题消失之前尝试过的最后一件事。

认为文件已损坏,我开始创建一个新文件,从据称已损坏的文件中导入代码和表单,从头开始创建工作表(而不是复制旧文件以避免任何复制损坏的风险),我意识到我有一个全局变量和一张名为ShNesting表。 重复名称不是问题,因为全局变量的范围比工作表对象的范围更窄,因此 VBA 从未见过工作表ShNesting 我检查了 git 存储库,我发现它已经使用重复名称工作了 5 年没有问题。

我将工作表重命名为Sheet4 ,问题就消失了。

我试图用损坏文件的备份副本重现该问题,但我无法重现它。 我不知道我是否无法重现它,因为备份副本的保存条件不会重现问题并且我无法重现它,或者我的计算机是否决定自行修复。

我等了几天,我从来没有遇到过这个问题,所以我想在这里留下一点更新。

不是真正的解决方案,但评论太长了。
我知道我见过更好的堆栈跟踪程序,但也许可以修改您的DoEvents2并在所有UDFSheetChange或您认为正在运行的任何例程的顶部添加对TraceRoutines的调用。 TraceRoutines上的超时用作断路器。

Public DoingEvents As Boolean
Public TraceList As String
Public LastTime As Single
Public StartTime As Single

Sub DoEvents2()
    StartTime = Timer
    LastTime = StartTime
    If DoingEvents Then
        Debug.Print "Nested DoEvents calls?"
        TraceList = TraceList & "DoEvents" & " (@ " & Format(Timer - LastTime, "0.000") & "s)" & vbCrLf
        Debug.Assert False
    Else
        TraceList = vbNullString
    End If
    DoingEvents = True
    DoEvents
    DoingEvents = False
    Debug.Print Format(Timer - StartTime, "0.000") & " Doing Events"
    Debug.Print TraceList
End Sub

Sub TraceRoutines(Name As String, Optional Timeout As Long = 20)
'Static LastTime As Single
Static TimeoutTriggered As Boolean
    If Timeout = 0 Then TimeoutTriggered = True
    If DoingEvents Then
        TraceList = TraceList & Name & " (@ " & Format(Timer - LastTime, "0.000") & "s)" & vbCrLf
        LastTime = Timer
        If LastTime - StartTime > Timeout Then
            If Not TimeoutTriggered Then
                Debug.Print TraceList
                MsgBox "What is going on here?"
                Debug.Assert False
                TimeoutTriggered = True
            End If
        Else
            'Reset
            TimeoutTriggered = False
        End If
    End If
End Sub

Sub MyFunction()
    TraceRoutines "MyFunction"
End Sub

我最近看到 VBA 代码表现出非常相似的行为,即它是已经可靠工作多年但后来开始偶尔挂起 Excel 的代码。最终我将问题隔离到对Application.DoEvents的调用,而不是采取预期的少数几毫秒的执行时间最多可能需要三分钟。

有问题的 PC 安装了 Netskope 防病毒软件,我发现如果停用 Netskope,问题就会消失。 尽管对我来说一个永久的解决方案是修改代码以不再使用DoEvents

所以在回答这个问题时:您是否尝试过关闭任何防病毒软件?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM