当前位置:  开发笔记 > 编程语言 > 正文

如何获取非当前线程的堆栈跟踪?

如何解决《如何获取非当前线程的堆栈跟踪?》经验,为你挑选了4个好方法。

可以使用System.Diagnostics.StackTrace获取堆栈跟踪,但必须暂停线程.暂停和恢复功能已经过时,所以我希望存在更好的方法.



1> Joe Albahari..:

到目前为止,这对我有用:

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上很常见.


Thread.Suspend()和Thread.Resume()在框架中被标记为过时,因此任何使用警告为错误的人都需要在方法之前使用``#pragma warning disable 0618``并且``#pragma warning restore 0618` `之后为了让这段代码得到编译.
还有一个很小的死锁风险:如果运行时决定在"ready.Wait()"和"targetThread.Suspend()"之间挂起主线程,你可能仍然会遇到死锁,因为回退线程退出已经.IMO你需要在解锁线程中有一个循环,只有当主线程发出信号表明它安全退出函数时才会离开.

2> Dirk Bonné..:

这是一个旧的线程,但只是想提出有关提议的解决方案的警告:暂停和恢复解决方案不起作用 - 我只是在我的代码中遇到了死锁,尝试序列Suspend/StackTrace/Resume.

问题是StackTrace构造函数执行RuntimeMethodHandle - > MethodBase转换,这会更改内部MethodInfoCache,它会锁定.发生了死锁,因为我正在检查的线程也正在进行反射,并持有该锁定.

遗憾的是,暂停/恢复内容不是在StackTrace构造函数内部完成的 - 然而这个问题很容易被绕过.



3> Andreas..:

正如我在评论中提到的那样,所提出的解决方案仍然存在很小的死锁概率.请在下面找到我的版本.

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"并不是必需的,但为什么在这种微妙的情况下会冒任何风险呢?



4> Brian Rasmus..:

根据Nutshell中的C#3.0,这是可以调用Suspend/Resume的少数情况之一.

推荐阅读
N个小灰流_701
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有