0

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();
   }
} 
skyde
  • 2,816
  • 4
  • 34
  • 53

2 Answers2

2

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
Ben Adams
  • 3,281
  • 23
  • 26
0

The only way I know of, is using ThreadPool.UnsafeQueueUserWorkItem:

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 tcs = new TaskCompletionSource<bool>();
            ThreadPool.UnsafeQueueUserWorkItem(async s =>
            {
                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);
                ((TaskCompletionSource<bool>)s).SetResult(true);
            }, tcs);
            await tcs.Task;
        });
        t1.Wait();
   }
} 
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59