当前位置: 首页 > 编程语言 > C#

UsingThreadStaticVariableswithasync-awaitShare

时间:2023-04-10 13:10:14 C#

UsingThreadStaticVariableswithasync/await借助C#中新的async/await关键字,现在了解如何(以及何时)使用ThreadStatic数据)有影响因为回调委托在与同时启动的异步操作不同的线程上执行。例如,下面这个简单的控制台应用程序:[ThreadStatic]privatestaticstringSecret;staticvoidMain(string[]args){Start().Wait();控制台.ReadKey();}privatestaticasyncTaskStart(){Secret="moomoo";Console.WriteLine("开始于线程[{0}]",Thread.CurrentThread.ManagedThreadId);Console.WriteLine("秘密是[{0}]",Secret);等待困倦();Console.WriteLine("在线程[{0}]上完成",Thread.CurrentThread.ManagedThreadId);Console.WriteLine("秘密是[{0}]",Secret);}privatestaticasyncTaskSleepy(){Console.WriteLine("Wasonthread[{0}]",Thread.CurrentThread.ManagedThreadId);等待任务。延迟(1000);Console.WriteLine("现在在线程[{0}]",Thread.CurrentThread.ManagedThreadId);}将输出以下内容:在线程[9]上启动,秘密是[moomoo]在线程[9]上,现在在线程[11]上完成,在线程[11]上完成,秘密是[]我还尝试使用CallContext.SetData和CallContext。GetData并获得相同的行为。在阅读了一些相关的问题和线程之后:似乎像ASP.Net这样的框架明确地跨线程迁移了HttpContext,而不是CallContext,所以同样的事情会发生在async和await关键字上吗?考虑到async/await关键字的使用,存储与可以(自动!)在回调线程上恢复的特定执行线程关联的数据的最佳方法是什么?谢谢,您可以使用CallContext.LogicalSetData和CallContext.LogicalGetData,但我不建议您这样做,因为它们在使用简单并行性时不支持任何类型的“克隆”(Task.WhenAny/Task.WhenAll)。我已经打开了一个UserVoice请求,以获得更完整的异步兼容“上下文”,在这篇MSDN论坛帖子中有更详细的解释。我们自己建造一个似乎是不可能的。JonSkeet有一篇关于这个主题的优秀博客文章。因此,我建议您使用参数、lambda闭包或本地实例(this)的成员,如Marc所述。是的,OperationContext.Current没有保留在await中。更新:.NET4.5支持异步代码中的Logical[Get|Set]Data。详细信息在我的博客上。基本上,我想强调:不要那样做。[ThreadStatic]永远不会很好地处理在线程之间跳转的代码。但你不必这样做。任务已经有状态——事实上,它可以用两种不同的方式实现:此外,编译器可以完成这里需要的所有操作:privatestaticasyncTaskStart(){stringsecret="moomoo";Console.WriteLine("开始于线程[{0}]",Thread.CurrentThread.ManagedThreadId);Console.WriteLine("Secretis[{0}]",secret);等待困倦();Console.WriteLine("在线程[{0}]上完成",Thread.CurrentThread.ManagedThreadId);Console.WriteLine("Secretis[{0}]",secret);}没有静力学;线程或多个任务没有问题。它只是工作。请注意,秘密不仅仅是“本地的”;编译器处理了一些巫术,比如使用迭代器块和捕获的变量。检查反射器,我得到:[CompilerGenerated]privatestructd__0:IAsyncStateMachine{//...这里没有显示更多publicstring5__1;任务需要同步提供程序才能在同一线程上继续执行。这是一个昂贵的词,一个简单的诊断是通过在调试器中查看System.Threading.SynchronizationContext.Current的值。该值在控制台模式应用程序中将为空。没有提供程序可以在控制台模式应用程序的特定线程上运行代码。只有Winforms或WPF应用程序或ASP.NET应用程序有提供者。而且只在他们的主线上。这些应用程序的主线程非常特殊,它们有一个调度程序循环(又名消息循环或消息泵)。这实现了生产者-消费者问题的通用解决方案。这是一个调度程序循环,允许一个线程做一些工作。这样的一点工作将是await表达式之后任务的延续。该位将在调度程序线程上运行。WindowsFormsSynchronizationContext是Winforms应用程序的同步提供程序。它使用Control.Begin/Invoke()来分派请求。对于WPF,它是DispatcherSynchronizationContext类,它使用Dispatcher.Begin/Invoke()来分派请求。对于ASP.NET,它是AspNetSynchronizationContext类,它使用不可见的内部管道。它们在初始化时创建各自提供程序的实例并将其分配给SynchronizationContext。当前控制台模式应用程序没有这样的提供程序。主要是主线程完全不适合,没有使用调度器循环。您可以创建自己的类,然后也可以创建自己的SynchronizationContext派生类。很难做到,您不能再调用Console.ReadLine(),因为它会完全冻结Windows调用中的主线程。您的控制台模式应用程序不再是控制台应用程序,它将开始像Winforms应用程序一样运行。请注意,这些运行时环境具有同步提供程序是有充分理由的。他们必须有一个,因为GUI基本上是线程不安全的。控制台没有问题,是线程安全的。看看这篇文章在标有ThreadStaticAttribute的字段上,初始化只发生一次。当在你的代码中创建一个ID为11的新线程时,会创建一个新的Secret字段,但是它是空的/null,当它返回到“开始”任务时,任务将在第11个线程完成(如打印输出),因此字符串为空。您可以通过在调用Sleepy之前将Secret存储在“Start”内的本地字段中来解决您的问题,然后在从Sleepy返回后从本地字段恢复Secret。您也可以在调用“awaitTask.Delay(1000);”之前在Sleepy中执行此操作。这实际上会导致线程切换。以上就是C#学习教程:ThreadStaticvariableswithasync/await的全部内容分享。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: