How to maintain Thread context across async await model in C#?

Ashwin picture Ashwin · May 9, 2014 · Viewed 8.1k times · Source

Is using ThreadStatic and setting the context every time await completes "an option"? Is there another way?

public async void Test()
{
    // This is in Thread 1
    Foo foo = new Foo();
    Context.context = "context1"; // This is ThreadStatic
    string result = await foo.CallAsynx();

    // This is most likely Thread 2
    Context.context = "context1";   // This might be a different thread and so resetting context    
}

Now is there another way if I don't want to use ThreadStatic?

Answer

Stephen Cleary picture Stephen Cleary · May 9, 2014

ThreadStatic, ThreadLocal<T>, thread data slots, and CallContext.GetData / CallContext.SetData do not work well with async, since they are thread-specific.

The best alternatives are:

  1. Passing it as an argument as @PauloMorgado suggested. Equivalently, you could set it as a field member of an object (it's implicitly passed as an argument via this); or you could have your lambdas capture the variable (underneath, the compiler will implicitly pass it as an argument via this).
  2. Use HttpContext.Items (if you are on ASP.NET 4.5).
  3. Use CallContext.LogicalGetData / CallContext.LogicalSetData as @Noseratio suggested. You can only store immutable data in the logical thread context; and it only works on .NET 4.5 and is not available on all platforms (e.g., Win8).
  4. Force all async continuations back to the same thread by installing a "main loop" for that thread, such as the AsyncContext from my AsyncEx library.