如何找到导致工作人员停止的 .NET 方法
Posted
技术标签:
【中文标题】如何找到导致工作人员停止的 .NET 方法【英文标题】:How to find what .NET method led caused the worker to stop 【发布时间】:2013-11-11 17:46:25 【问题描述】:我的代码已死锁。这是主线程的堆栈跟踪:
[托管到本地转换]
WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd,int uMsgFilterMin,int uMsgFilterMax) + 0x14 字节 WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(参考 System.Windows.Interop.MSG 消息,System.IntPtr hwnd,int minMessage, int maxMessage) + 0x80 字节 WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame 帧)+ 0x75 字节 WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame 帧)+ 0x49 字节 WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4b 字节 PresentationFramework.dll!System.Windows.Application.RunDispatcher(对象 忽略)+ 0x17 字节 PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window 窗口)+ 0x6f 字节 PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window 窗口) + 0x26 字节 PresentationFramework.dll!System.Windows.Application.Run() + 0x1b 字节 MainDashboard.exe!MainDashboard.App.Main() + 0x59 字节 C# [本地到托管转换] [托管到本地转换] mscorlib.dll!System.AppDomain.ExecuteAssembly(字符串程序集文件, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x6b 字节 Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x27 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object 状态)+ 0x6f 字节 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0xa7 字节 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0x16 字节 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 状态)+ 0x41 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes [本机到托管转换]
我如何知道我的代码的哪一部分负责。应用入口点是:
MainDashboard.exe!MainDashboard.App.Main() + 0x59 字节 C#
这是堆栈跟踪中唯一来自我的代码的行。
尝试查看其他两个帧的调用堆栈会显示以下内容:
当前线程当前未运行代码所有调用堆栈是 无法获取。
这里是另一个工作线程的调用栈:
[托管到本地转换]
System.dll!Microsoft.Win32.SystemEvents.WindowThreadProc() + 0xaf 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(对象 状态)+ 0x6f 字节 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0xa7 字节 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0x16 字节 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 状态)+ 0x41 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes [本机到托管转换]
这里是最后一个线程的调用栈:
[托管到本地转换]
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.WaitForThreadExit() + 0x93 字节 Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunParkingWindowThread() + 0x253 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(对象 状态)+ 0x6f 字节 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0xa7 字节 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 state, bool preserveSyncCtx) + 0x16 字节 mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext、System.Threading.ContextCallback 回调、对象 状态)+ 0x41 字节 mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes [本机到托管转换]
应用已挂起。它总共需要处理 49,000 条记录。它停在3,029。该应用程序此时未使用任何资源。任务管理器显示它使用 0% CPU。 UI 是响应式的,但它的设计始终是响应式的。
此外,这不是确定性的。我的意思是,如果我重新启动应用程序,它会在代码中的不同位置挂起,因此没有一条记录存在导致整个事情崩溃的问题。
【问题讨论】:
堆栈跟踪如何查找其他线程? 某些版本的 Visual Studio 有一个并发可视化工具,应该可以做你想做的事。 @kkokosa,我用其他调用堆栈编辑了这个问题。 处理已停止的事实并不意味着存在任何死锁。这可能与您的主处理循环不正确地获取break
一样简单,或者引发了一些异常并且一些外部try/catch
已跳出您的主处理循环并因此停止了该过程。如果您连接了调试器,请检查输出是否有任何first-chance exception
通知。任何 NullReference、ArgumentOutOfRange、NotImplemented、InvalidOperation 等都可能是错误处理错误的标志,只会停止循环。
将try ... catch
放入您的线程并让他们记录异常情况如何?
【参考方案1】:
由于您现在在“输出”中注意到“第一次机会异常”并且它们似乎会杀死您的线程,因此除了 try/catch/log 之外,还有一件事。
进一步使用 VS/CLR 的调试功能。转到 DEBUG 菜单,然后找到 EXCEPTIONS 然后找到一个(或全部,但我更喜欢一个接一个)您识别为抛出并杀死您的线程的异常,即 InvalidOperationException 并检查勾选“throw " 或 "未处理" (取决于您想要什么以及您拥有的 VS 版本)。
现在,假设您勾选了“抛出”,IDE 将自动中断/停止程序,只要它试图抛出这种类型的异常。
..它不仅会停止程序,还会像你放置断点一样跳转到那里。您将立即获得您想要检查的所有堆栈跟踪、变量、代码等。
但是,另一方面,如果您的应用程序正在以每秒数千的速率发送这种异常的垃圾邮件,只是因为有人懒惰并且 if/else 遇到了一些错误的参数情况,而是尝试/捕获了异常,然后试图依靠这个被“抛出”的异常将..好吧..失败。在这种情况下,您可能想尝试使用“未处理”的勾号(如果您在 IDE 中看到它),但当然它也有其自身的局限性。尽管如此,如果您可以将 IDE 附加到失败的进程,它们确实是非常宝贵的工具。 Insta-break 在有问题的未知线路上!
【讨论】:
【参考方案2】:听起来您在识别应用中发生的事情时遇到了麻烦,而且您没有日志记录......
http://logging.apache.org/log4net/
在尝试解决这类问题时,日志文件非常宝贵。
实现 Log4net,将其配置为写入日志文件并开始记录内容...
例如在方法开始时您怀疑可能涉及的地方
private void MethodName()
logger.Debug("Begin MethodName");
以及方法的结束
logger.Debug("End MethodName");
很快你就会得到你需要的线索。
您可以自定义实际记录的内容,以便获得详细的调试记录或仅记录错误。
如果您还没有使用 Visual Studio 的“调试位置”工具栏,请使用它。
【讨论】:
log4net 不是控制台输出更好的解决方案。持久日志记录的优势在于已部署的系统 - 没有附加调试器。这根本不是答案。 在这种情况下使用调试器是没有用的,在多线程应用程序中设置断点会改变时间,从而改变应用程序的行为。因此,正如您自己在无法附加调试器的情况下所说的那样,日志记录是他的最佳选择 Log4net 将自动为您记录线程号,以便您确定哪个线程负责记录。 Console.Write 不这样做。 Log4net 允许您以多种方式输出日志,并且以最少干扰且不太可能干扰程序正常运行的方式进行。 Console.Write 不这样做。 Log4net 可以通过配置打开和关闭,Console.Write 不这样做以上是关于如何找到导致工作人员停止的 .NET 方法的主要内容,如果未能解决你的问题,请参考以下文章