可以使用System.Diagnostics.StackTrace获取堆栈跟踪,但必须暂停线程.暂停和恢复功能已经过时,所以我希望存在更好的方法.
到目前为止,这对我有用:
StackTrace GetStackTrace (Thread targetThread) { StackTrace stackTrace = null; var ready = new ManualResetEventSlim(); new Thread (() => { // Backstop to release thread in case of deadlock: ready.Set(); Thread.Sleep (200); try { targetThread.Resume(); } catch { } }).Start(); ready.Wait(); targetThread.Suspend(); try { stackTrace = new StackTrace (targetThread, true); } catch { /* Deadlock */ } finally { try { targetThread.Resume(); } catch { stackTrace = null; /* Deadlock */ } } return stackTrace; }
如果它死锁,则自动释放死锁并返回空跟踪.(然后你可以再次调用它.)
我应该补充说,经过几天的测试,我只能在我的Core i7机器上创建死锁.但是,当CPU以100%运行时,死锁在单核VM上很常见.
这是一个旧的线程,但只是想提出有关提议的解决方案的警告:暂停和恢复解决方案不起作用 - 我只是在我的代码中遇到了死锁,尝试序列Suspend/StackTrace/Resume.
问题是StackTrace构造函数执行RuntimeMethodHandle - > MethodBase转换,这会更改内部MethodInfoCache,它会锁定.发生了死锁,因为我正在检查的线程也正在进行反射,并持有该锁定.
遗憾的是,暂停/恢复内容不是在StackTrace构造函数内部完成的 - 然而这个问题很容易被绕过.
正如我在评论中提到的那样,所提出的解决方案仍然存在很小的死锁概率.请在下面找到我的版本.
private static StackTrace GetStackTrace(Thread targetThread) { using (ManualResetEvent fallbackThreadReady = new ManualResetEvent(false), exitedSafely = new ManualResetEvent(false)) { Thread fallbackThread = new Thread(delegate() { fallbackThreadReady.Set(); while (!exitedSafely.WaitOne(200)) { try { targetThread.Resume(); } catch (Exception) {/*Whatever happens, do never stop to resume the target-thread regularly until the main-thread has exited safely.*/} } }); fallbackThread.Name = "GetStackFallbackThread"; try { fallbackThread.Start(); fallbackThreadReady.WaitOne(); //From here, you have about 200ms to get the stack-trace. targetThread.Suspend(); StackTrace trace = null; try { trace = new StackTrace(targetThread, true); } catch (ThreadStateException) { //failed to get stack trace, since the fallback-thread resumed the thread //possible reasons: //1.) This thread was just too slow (not very likely) //2.) The deadlock ocurred and the fallbackThread rescued the situation. //In both cases just return null. } try { targetThread.Resume(); } catch (ThreadStateException) {/*Thread is running again already*/} return trace; } finally { //Just signal the backup-thread to stop. exitedSafely.Set(); //Join the thread to avoid disposing "exited safely" too early. And also make sure that no leftover threads are cluttering iis by accident. fallbackThread.Join(); } } }
我认为,ManualResetEventSlim"fallbackThreadReady"并不是必需的,但为什么在这种微妙的情况下会冒任何风险呢?
根据Nutshell中的C#3.0,这是可以调用Suspend/Resume的少数情况之一.