Search code examples
.netasync-awaittask-parallel-librarythread-local-storagecallcontext

How to prevent task from inheriting parent task Logical Call context


I am trying to use AsyncLocal as a replacement for Thread local storage when using Task.Run() and Async methods. The problem I have is that i need the code below to print

from t1 t1
from t1 t1
from t2 t2
from t2 t2

This would be the behavior if using Thread local storage, but instead I am getting this output.

from t1 t1
from t1 t1
from t2 t1
from t2 t1

Example code:

public class ClientClass {

   public static void Main() 
   {
      AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();
      var t1 = Task.Run( async () => {
          string a = _asyncLocalString.Value;
          if (a == null) {
              _asyncLocalString.Value = "t1";
          }
          a = _asyncLocalString.Value;
          Console.WriteLine("from t1 " + a);
          await Task.Delay(10);
          string b = _asyncLocalString.Value;
          Console.WriteLine("from t1 " + b);
          var t2 = Task.Run( async () => {
              string aa = _asyncLocalString.Value;
              if (aa == null) {
                  _asyncLocalString.Value = "t2";
              }
              aa = _asyncLocalString.Value;
              Console.WriteLine("from t2 " + aa);
              await Task.Delay(10);
              string bb = _asyncLocalString.Value;
              Console.WriteLine("from t2 " + bb);

          });
          await t2;
      });
      t1.Wait();
   }
} 

Solution

  • You can Suppress the flow prior to calling Task.Run and restore it after

    public class ClientClass {
    
       public static void Main() 
       {
            AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();
            var t1 = Task.Run(async () =>
            {
                string a = _asyncLocalString.Value;
                if (a == null)
                {
                    _asyncLocalString.Value = "t1";
                }
                a = _asyncLocalString.Value;
                Console.WriteLine("from t1 " + a);
                await Task.Delay(10);
                string b = _asyncLocalString.Value;
                Console.WriteLine("from t1 " + b);
    
                ExecutionContext.SuppressFlow();
    
                var t2 = Task.Run(async () =>
                {
                    string aa = _asyncLocalString.Value;
                    if (aa == null)
                    {
                        _asyncLocalString.Value = "t2";
                    }
                    aa = _asyncLocalString.Value;
                    Console.WriteLine("from t2 " + aa);
                    await Task.Delay(10);
                    string bb = _asyncLocalString.Value;
                    Console.WriteLine("from t2 " + bb);
    
                });
    
                ExecutionContext.RestoreFlow();
    
                await t2;
            });
            t1.Wait();
       }
    } 
    

    Giving

    from t1 t1
    from t1 t1
    from t2 t2
    from t2 t2