昨天我遇到了一个非常奇怪的错误,一天之后我几乎没有取得任何进展,所以我觉得它是一个很好的候选人,可以询问社区。我会问一些问题因为我认为它只是一个。
我有一个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反汇编:
比我代码的最后一帧,m_SettingCache是一个字典,它不包含请求的密钥:
接下来的几帧:
我认为代码非常简单,只是通用设置读取默认值。如果出现问题(设置名称未定义或无法转换),将返回默认值。代码肯定有效。我从转储中看到的是字典中的读取永远不会返回,尽管它应该抛出一个KeyNotFoundException但是从未发生过。有什么建议吗?
注意:主线程确实在转储捕获的状态下停止:每次进行转储时结果都是一样的。
注意2:在第一次执行此代码路径时,挂起永远不会发生,在每个场景中,在挂起之前执行了相同的代码路径(从应用程序日志中推断出来)
我会根据要求提供更多详情。 提前谢谢。
修改
CFAPControlLibrary.dll是应用程序的主要程序集。它包含Windows窗体及其相应的逻辑。通过WCF实现与服务器的通信。并且使用BackgroundWorker在并行线程中进行更大的请求。您在调用堆栈中看到的执行路径由此类BackgroundWorker的completition事件调用。
我粘贴了请求的代码位here
我的AppDomain.CurrentDomain.UnhandledException处理程序是here
堆栈的一部分,我认为首先是无效的,但后来被证明是重要的(敏感的字符串文字从图像中删除):
这表明调用了Application.Run,我不知道它为什么没有显示在调用堆栈中。
更新
花了三天没有找到问题的原因我决定尝试一种解决方法。由于内存转储显示应用程序始终在同一点挂起:应该抛出KeyNotFound异常。最直接的解决方法是重构代码,尽可能不抛出。该版本通过了测试,从未挂起。 这根本不是解决方案,但我们不能再花时间在这上面了。所以基本上我用手指交叉代码,希望我再也不会看到这次崩溃了。
感谢您提出的所有建议
答案 0 :(得分:4)
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泵可以避免麻烦。执行此操作的代码将隐藏在[外部代码]部分中。
在发布的代码中肯定有证据表明它在非常不合适的地方使用 lock 。在UI代码中使用 lock 永远不会正确。
当应用程序崩溃时看到死锁也很容易解释。
这是代码中一个严重的结构问题,你需要修复它。从Main()方法开始,这很早就出错了。也可以轻松检查您的开发机器,只需查看调用堆栈。