繁体   English   中英

如何在 C# 中跨异步等待模型维护线程上下文?

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

每次等待完成“一个选项”时是否使用 ThreadStatic 并设置上下文? 还有其他方法吗?

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    
}

如果我不想使用 ThreadStatic,现在还有另一种方法吗?

ThreadStaticThreadLocal<T> 、线程数据槽和CallContext.GetData / CallContext.SetData不适用于async ,因为它们是特定于线程的。

最好的选择是:

  1. 正如@PauloMorgado 建议的那样,将其作为参数传递。 等效地,您可以将其设置为对象的字段成员(通过this作为参数隐式传递); 或者你可以让你的 lambdas 捕获变量(在下面,编译器将通过this作为参数隐式传递)。
  2. 使用HttpContext.Items (如果您使用的是 ASP.NET 4.5)。
  3. 使用CallContext.LogicalGetData / CallContext.LogicalSetData作为 @Noseratio 建议。 您只能在逻辑线程上下文中存储不可变数据; 并且它仅适用于 .NET 4.5,并非适用于所有平台(例如,Win8)。
  4. 通过为该线程安装“主循环”,例如我的 AsyncEx 库中AsyncContext强制所有async延续回到同一线程。

只是如果有人在几年后有同样的问题并发现这个线程......

有一个新功能叫做

AsyncLocal<T>

https://docs.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?view=netcore-3.1

这适用于“异步/等待”,也适用于:

  • 任务.运行(...)
  • Dispatcher.BeginInvoke(...)
  • 新线程(...)。开始()

我只是用以下代码测试这三个:

    private void StartTests() {
        Thread.Sleep(1000);
        Task.Run(() => DoWork1());
        Task.Run(() => DoWork2());
    }

    private void DoWork1() {
        ThreadContext.Context.Value = "Work 1";
        Thread.Sleep(5);
        Task.Run(() => PrintContext("1"));
        Thread.Sleep(10);
        Dispatcher.BeginInvoke(new Action(() => PrintContext("1")));
        Thread.Sleep(15);
        var t = new Thread(() => PrintContextT("1"));
        t.Start();
    }

    private void DoWork2() {
        ThreadContext.Context.Value = "Work 2";
        Task.Run(() => PrintContext("2"));
        Thread.Sleep(10);
        Dispatcher.BeginInvoke(new Action(() => PrintContext("2")));
        Thread.Sleep(10);
        var t = new Thread(() => PrintContextT("2"));
        t.Start();
    }

    private void PrintContext(string c) {
        var context = ThreadContext.Context.Value;
        Console.WriteLine("P: " + context + "-" + c);

        Task.Run(() => PrintContext2(c));
    }

    private void PrintContext2(string c) {
        Thread.Sleep(7);
        var context = ThreadContext.Context.Value;
        Console.WriteLine("P2: " + context + "-" + c);
    }

    private void PrintContextT(string c) {
        var context = ThreadContext.Context.Value;
        Console.WriteLine("T: " + context + "-" + c);
    }

    public class ThreadContext {
        public static AsyncLocal<object> Context = new AsyncLocal<object>();
    }

输出:

P: 作品 2-2

P: 工作 1-1

P2:工作 2-2

P: 作品 2-2

P2:工作 1-1

P: 工作 1-1

P2:工作 2-2

T: 工作 2-2

P2:工作 1-1

T: 工作 1-1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM