在C#中唯一识别方法调用堆栈

时间:2015-03-18 08:53:10

标签: c# multithreading asynchronous

我正在编写一个应用程序,我需要记录现有代码。代码在并行环境中运行。出于记录目的,我需要找到彼此不同的调用栈,例如:

public class ThreadedLogicClass
{
    public void MethodOneThreaded()
    {
        StackTrace trace = new StackTrace();
        StackFrame frame = trace.GetFrame(1);
        MethodBase methodBase = frame.GetMethod();

        Thread thread = new Thread(new ThreadStart(this.ThreadedLogic));
        thread.Start();
    }

    public void MethodOneNonThreaded()
    {
        StackTrace trace = new StackTrace();
        StackFrame frame = trace.GetFrame(1);
        MethodBase methodBase = frame.GetMethod();

        this.NonThreadedLogic();
    }

    private void ThreadedLogic()
    {
        StackTrace trace = new StackTrace();
        StackFrame frame = trace.GetFrame(1);
        MethodBase methodBase = frame.GetMethod();

        Thread thread = new Thread(new ThreadStart(this.LastCall));
        thread.Start();
    }

    private void NonThreadedLogic()
    {
        StackTrace trace = new StackTrace();
        StackFrame frame = trace.GetFrame(1);
        MethodBase methodBase = frame.GetMethod();

        this.LastCall();
    }

    private void LastCall()
    {
        StackTrace trace = new StackTrace();
        StackFrame frame = trace.GetFrame(1);
        MethodBase methodBase = frame.GetMethod();
    }
}

考虑这个主要计划:

ThreadedLogicClass obj = new ThreadedLogicClass();
Console.WriteLine();
Console.WriteLine(" =======> Calling MethodOneThreaded");
obj.MethodOneThreaded();
Console.WriteLine(" =======> End Calling MethodOneThreaded");
Console.WriteLine();
Console.WriteLine(" =======> Calling MethodOneNonThreaded");
obj.MethodOneNonThreaded();
Console.WriteLine(" =======> End Calling MethodOneNonThreaded");

从这里我需要识别两个独特的调用堆栈及其各种端点。

1。)MethodOneThreaded - > ThreadedLogic - >最后一次通话 2.)MethodOneNonThreaded - > NonThreadedLogic - >最后呼叫

我想要的是调用方法调用可能像主方法中的开始/结束调用一样,用于记录上下文以及可能与上下文中的对象关联的一些数据。

有人可以引导我到一条路线,在那里我可以找到有关如何识别这两者的更多信息。

识别的原因是创建单独的上下文来存储数据,直到调用堆栈结束,我需要维护该数据以便以后用于日志服务。

任何帮助都将受到高度赞赏。

1 个答案:

答案 0 :(得分:2)

您可以使用StackFrameStackTrace诊断类:

var frames = new StackTrace().GetFrames();

这将为您提供有关整个调用堆栈的信息,包括IL和本机代码偏移。如果您还需要代码行号,则需要提供调试信息(pdb s)。

请注意,优化可以使这一点相当令人惊讶,因为它将彻底摆脱许多方法调用。如果您有使用WinDbg和类似工具的经验,只要保留原始版本中的pdb,就可以轻松获取偏移中的行号。

<强>更新

哦,你想在线程启动时识别调用堆栈 ?我很害怕你运气不好。除了在Thread.Start调用周围创建公共包装类之外,没有简单的方法可以处理在线程内传递所需信息。 .NET(也不是Windows)不会跟踪谁开始了哪个线程以及在哪里开始。

这仍然可以做到看起来有点优雅。如果我们假设代码中没有await s,您可以执行以下操作:

void Main()
{
  ThreadHelper.Start(() => Console.WriteLine(ThreadContext.CallerTrace.ToString()));
}

public static class ThreadHelper
{
  public static void Start(Action action)
  {
    var capturedStack = new StackTrace(1);

    new Thread(() => { ThreadContext.CallerTrace = capturedStack; action(); })
        .Start();
  }
}

public static class ThreadContext
{
  [ThreadStatic]
  public static StackTrace CallerTrace;
}

使用ThreadHelper.Start代替new Thread(...).Start(),您可以确保在启动新线程之前捕获堆栈信息,然后传入内部。 ThreadStatic静态字段可以从任何地方访问,而不同的线程可以访问。您当然希望加入实际调用堆栈与ThreadContext.CallerTrace 中捕获的调用堆栈(如果首先存在)。

扩展这个以“开始......结束”的方式工作应该是非常微不足道的。您可以使用“假”一次性用品,并执行以下操作:

using (MethodCallLogger.Log(MethodInfo.GetCurrentMethod()))
{
  // Do your stuff
}

MethodCallLogger.Log的返回值可以是在构造函数中写入“begin”的简单类,而在dispose方法中写入“end”。它当然可以访问完整的堆栈跟踪,包括ThreadContext.CallerTrace中捕获的跟踪。

如果你想要包装方法调用,你可以创建一个简单的帮助器方法:

public static class MethodCallHelper
{
  public static void Log(Action action)
  {
    try
    {
      Console.WriteLine("Begin " + action.Method.ToString());

      action();
    }
    finally
    {
      Console.WriteLine("End " + action.Method.ToString());
    }
  }
}