Persist thread's context across async calls

时间:2015-05-04 19:22:43

标签: c#

I am trying to disentangle my logs by grouping my log messages together. Ideally, I'd append to each message some identifier indicating that it is part of a group of commands achieving some piece of work.

If this were a single threaded application, the thread's ID would be a candidate. However, this solution makes heavy use of async Tasks, which precludes its use. Is it possible to get something like the following to work, where every execution of Main() would always print out the same thread ID?

static async void Main()
{
    var t = Task.Run(async () => await MyAsyncMethod());
    await t;
}

private static async Task MyAsyncMethod()
{
  Debug.WriteLine("Log message: {0}", Thread.CurrentThread.ManagedThreadId);

  await Task.Delay(1000);

  Debug.WriteLine("Log message: {0}", Thread.CurrentThread.ManagedThreadId);

  await Task.Delay(1000);

  Debug.WriteLine("Log message: {0}", Thread.CurrentThread.ManagedThreadId);
}

1 个答案:

答案 0 :(得分:4)

最好的方法IMO是@SriramSakthivel建议的:只需生成一个id并传递它。如果您有“消息”类型,则只需将其添加到该类型即可。

但是,如果这会导致代码更改太多,那么您可以隐式传递它。我有blog post with the gory details;一般的想法是将数据存储为逻辑调用上下文的一部分:

public static class MyContext
{
  private static readonly string name = Guid.NewGuid().ToString("N");

  private sealed class Wrapper : MarshalByRefObject
  {
    public Guid Value { get; set; }
  }

  public static Guid CurrentContext
  {
    get
    {
      var ret = CallContext.LogicalGetData(name) as Wrapper;
      return ret == null ? Guid.Empty : ret.Value;
    }

    set
    {
      CallContext.LogicalSetData(name, new Wrapper { Value = value });
    }
  }
}

然后您可以在代码中使用它:

private static async Task MyAsyncMethod()
{
  MyContext.CurrentContext = Guid.NewGuid();

  Debug.WriteLine("Log message: {0}", MyContext.CurrentContext);

  await Task.Delay(1000);

  Debug.WriteLine("Log message: {0}", MyContext.CurrentContext);

  await Task.Delay(1000);

  Debug.WriteLine("Log message: {0}", MyContext.CurrentContext);
}

注意这种方法有两个重要的假设:

  1. 您的代码在.NET 4.5或更高版本上运行。
  2. 存储的数据是不可变的(在这种情况下是正确的,因为Guid是不可变的。)
  3. 它看起来像.NET 4.6中的will be builtin