3

昨天我遇到了一个非常奇怪的错误,一天后我几乎没有任何进展,所以我想这是询问社区的好人选。我会要求一些patiecne,因为我认为这是一个虽然。

我有一个 C# Winforms 应用程序,在生产中单击几下后就会挂起。仅在生产环境中,开发环境中永远不会发生同样的情况。当挂起发生时,实际上什么也没有发生(没有错误消息,但是根据任务管理器,任务进入“无响应”状态)但 GUI 变得无响应。我在相同的环境中尝试过,我可以确认行为。

不幸的是,无法在 prod 环境中安装开发工具和调试应用程序。我能做的最好的事情就是在应用程序停止时从应用程序中进行内存转储。问题是我完全不明白我在故障转储中看到的内容:我的主线程(GUI 线程)似乎卡在我找不到任何原因的指令中。

这是我的主线程的堆栈跟踪:

KERNELBASE.dll!_RaiseException@16()  + 0x54 bytes    
[External Code]    
CFAPControlLibrary.dll!CFAPControlLibrary.Communication.Base.GetSetting(string settingName) Line 850 + 0x10 bytes    C#
CFAPControlLibrary.dll!CFAPControlLibrary.ConfigHelper.Get<CFAPControlLibrary.DataTypes.ActionSortingOption>(string settingName) Line 25 + 0x35 bytes    C#
CFAPControlLibrary.dll!CFAPControlLibrary.ConfigHelper.Get<CFAPControlLibrary.DataTypes.ActionSortingOption>(string settingName, CFAPControlLibrary.DataTypes.ActionSortingOption defaultVal) Line 15 + 0x9 bytes    C#    CFAPControlLibrary.dll!CFAPControlLibrary.DataTypes.ActionStorage.Sort(System.Collections.Generic.List<CFAPControlLibrary.DataTypes.ActionClass> subject) Line 167 + 0xe bytes    C#
CFAPControlLibrary.dll!CFAPControlLibrary.DataTypes.ActionStorage.GetByStatus(string pStatus) Line 162 + 0x46 bytes    C#
CFAPControlLibrary.dll!CFAPControlLibrary.ActionSelector.FillNodes() Line 48 + 0x26 bytes    C#
CFAPControlLibrary.dll!CFAPControlLibrary.CFAPMain.OnActionDetailsArrived(CFAPControlLibrary.CFAPMain.RawActionDetails bwr) Line 371 + 0x10 bytes    C#
CFAPControlLibrary.dll!CFAPControlLibrary.CFAPMain.OnGetDetailsCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) Line 337 + 0xb bytes    C#
user32.dll!_InternalCallWinProc@20()  + 0x23 bytes    
user32.dll!_UserCallWinProcCheckWow@32()  + 0xb3 bytes    
user32.dll!_DispatchMessageWorker@8()  + 0xe6 bytes    
user32.dll!_DispatchMessageW@4()  + 0xf bytes    
[External Code]    
CFAPHost.exe!CFAPHost.Program.Main(string[] args) Line 50 + 0x1d bytes    C#
[External Code]    
mscoreei.dll!__CorExeMain@0()  + 0x38 bytes    
mscoree.dll!_ShellShim__CorExeMain@0()  + 0x227 bytes    
mscoree.dll!__CorExeMain_Exported@0()  + 0x8 bytes    
kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes    
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes    
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

这里是我来自顶部堆栈帧的源代码: KernelBase.dll 的反汇编: 来自 KernelBase.dll 的框架

比我的代码中的最后一帧,m_SettingCache 是一个字典,它不包含请求的键: Base.GetSetting

接下来的几帧: 来自 KernelBase.dll 的框架 来自 KernelBase.dll 的框架 来自 KernelBase.dll 的框架

我认为代码非常简单,它只是具有默认值的通用设置读取。如果出现问题(设置名称未定义或无法转换),将返回默认值。该代码肯定有效。我从转储中看到的是从字典中读取的内容永远不会返回,尽管它应该抛出 KeyNotFoundException 但这永远不会发生。有什么建议么?

注意:主线程确实在转储捕获的状态下停止:每次我进行转储时结果都是一样的。

注意2:在第一次执行此代码路径时不会发生挂起,在每种情况下,在挂起之前都会执行相同的代码路径(从应用程序日志中推断)

我将根据要求提供更多详细信息。提前致谢。

编辑:

CFAPControlLibrary.dll 是应用程序的主要程序集。它包含窗口窗体及其相应的逻辑。与服务器的通信是通过 WCF 实现的。更大的请求是在使用 BackgroundWorker 的并行线程中发出的。您在调用堆栈中看到的执行路径是由此类 BackgroundWorker 的完成事件调用的。

我在这里粘贴了请求的代码位

我的 AppDomain.CurrentDomain.UnhandledException 处理程序在这里

我首先认为无关紧要但后来证明很重要的堆栈部分(敏感的字符串文字已从图像中删除):

Application.Run 的证据 这表明 Application.Run 被调用,我不知道为什么它没有显示在调用堆栈中。

更新

在花了三天没有找到问题的原因后,我决定尝试一种解决方法。由于内存转储显示应用程序总是在同一点挂起:应该抛出 KeyNotFound 异常。最直接的解决方法是重构该代码以尽可能不抛出。该版本通过了测试并且从未挂起。这根本不是一个解决方案,但我们不能再花时间在这上面了。所以基本上我交叉手指发送代码,希望我再也不会看到这个崩溃。

谢谢大家的建议

4

1 回答 1

5
user32.dll!_DispatchMessageW@4()  + 0xf bytes    
[External Code]    
CFAPHost.exe!CFAPHost.Program.Main(string[] args) Line 50 + 0x1d bytes    C#

改写。这部分堆栈跟踪存在严重错误。Main() 方法应始终调用 Application.Run() 以开始泵送消息循环。或者应该存在 ShowDialog() 调用,这是可以分派消息的两种正常方式。两者都不存在,但是无论如何都会调用 DispatchMessage() winapi 函数。

有一种非常模糊的其他方式可以将消息发送到 CLR 中。当应用程序在 [STAThread] 上使用lock语句时会发生这种情况,例如 GUI 应用程序的主线程。或者 WaitHandle.WaitOne() 或 Thread.Join(),其他常见的阻塞方法。阻塞一个 STA 线程是非法的,因为它很可能导致死锁,所以 CLR 泵来避免麻烦。执行此操作的代码将隐藏在 [External Code] 部分中。

在发布的代码中肯定有证据,它在非常不合适的地方使用了。在 UI 代码中使用锁定永远不会正确。

在应用程序崩溃时看到死锁也很容易解释。

这是代码中的一个严重的结构性问题,您需要修复它。从 Main() 方法开始,这很早就出错了。也很容易检查您的开发机器,只需查看调用堆栈。

于 2013-09-25T12:14:22.910 回答