如何防止任务继承父任务逻辑调用上下文

时间:2018-11-27 18:03:33

标签: .net async-await task-parallel-library thread-local-storage callcontext

在尝试使用Task.Run()和Async方法时,我试图使用AsyncLocal替代线程本地存储。 我的问题是我需要下面的代码才能打印

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

如果使用Thread本地存储,这将是这种行为,但我却得到了此输出。

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

示例代码:

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();
   }
} 

2 个答案:

答案 0 :(得分:1)

您可以在调用Task之前先抑制流,然后在运行之后再运行并恢复

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();
   }
} 

给予

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

答案 1 :(得分:0)

我知道的唯一方法是使用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();
   }
}