6

每次等待完成“选项”时是否使用 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,现在还有另一种方法吗?

4

2 回答 2

17

ThreadStatic, ThreadLocal<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. 通过为该线程安装“主循环”来强制所有async延续回到同一线程,例如AsyncContext来自我的 AsyncEx library
于 2014-05-09T12:04:00.310 回答
5

如果有人在几年后有同样的问题并找到这个线程......

有一个新功能叫做

AsyncLocal<T>

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

这适用于“async/await”,也适用于:

  • 任务.运行(...)
  • 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

于 2020-06-18T14:53:41.693 回答