3

我们有一个相当大的现有代码库,用于构建在 ASP.NET 之上的各种 web 服务,并且该代码大量使用访问HttpContext.Current.User(包装为Client.User),我相当确定在内部使用它[ThreadStatic]来为您提供环境范围。

我目前正在研究是否有可能我们开始以 的形式使用更多的异步代码,async/await但我很难找到如何[ThreadStatic]适应这种情况。由于它的大量使用,消除对它的依赖[ThreadStatic]是不可能的。

我的理解是,当 anawait被命中时,代码的执行会停在那里,调用立即返回,并且设置了一个延续以在异步代码返回时继续执行。同时,原始线程可以自由地用于其他事情,例如处理另一个请求。到目前为止我对它的理解。

我无法真正找到明确的答案是HttpContext.Current.Userawait.

所以基本上:

HttpContext.Current.User = new MyPrincipal();
var user = HttpContext.Current.User;

await Task.Delay(30000);

// Meanwhile, while we wait for that lots of other requests are being handled, 
// possibly by this thread.

Debug.Assert(object.ReferenceEquals(HttpContext.Current.User, user));

Debug.Assert能保证成功吗?

如果另一个请求由与待处理的线程相同的线程Task.Delay处理,则该请求将设置一个不同的HttpContext.Current.User,那么当调用 continuation 时,以前的状态是否以某种方式存储和恢复?


我可以想象发生的事情是,在幕后,[ThreadStatic]状态作为某种字典保存在线程本身上,并且当一个线程在返回后返回线程池时,await该字典被保存在某个地方安全并返回到线程时它执行延续(或者在一个线程上,我不确定它是否一定是处理延续的同一个线程),可能是在屁股上鼓励拍拍和“去拿他们的男孩!”,但最后部分可能只是我的想象。

这有点准确吗?

更新:我试图组织一个小测试来尝试这个。到目前为止,它似乎有效,并且对于数百个请求中的任何一个,断言都没有失败。任何人都可以验证测试是否有意义?

https://gist.github.com/anonymous/72d0d6f5ac04babab7b6

4

3 回答 3

8

async/await与线程无关,这意味着它们具有可以在多个不同线程系统中工作的约定。

通常在/ThreadStatic中将无法正常工作,除了一些琐碎的情况,例如将在 UI 线程上恢复的 UI 上下文。对于 ASP.NET,不兼容.asyncawaitawaitThreadStaticasync

不过,HttpContext.Current是特例。ASP.NET 定义了一个“请求上下文”(由AspNetSynchronizationContext分配给的实例表示SynchronizationContext.Current)。默认情况下,await执行任务将捕获此同步上下文并使用它来恢复方法。当方法恢复时,它可能在不同的线程上,但会具有相同的请求上下文(包括HttpContext.Current文化和安全等其他内容)。

因此,HttpContext.Current被保留,但您自己的任何ThreadStatic值都没有。

我在介绍await中描述了如何使用。如果您想了解更多详细信息,请查看我的MSDN 文章(特别是关于异步 CTP 的最后一节)SynchronizationContextasyncSynchronizationContext

于 2014-10-23T00:11:21.817 回答
1

ThreadStatic 本身与异步代码完全不同。它通常在该上下文中用于提高性能,方法是为每个线程提供其自己的特定对象副本,从而消除争用。

当然,必须小心使用。如果你有一个场景,无论代码在哪个线程中执行,都需要使用相同的对象实例,那么 ThreadStatic 要么不起作用,要么需要仔细处理线程以确保每个执行流都来回到它所属的线程。

某些async/await 场景中,您可以保证继续发生在与原始 await 相同的线程上。例如,当您在 Forms 或 WPF 程序的 GUI 线程中等待时。但这通常不能由 async/await 特性保证。

最终,虽然您可以将 ThreadStatic 与 async/await 一起使用,但您仍然需要确保按照它的意思使用它:将特定值或对象绑定到特定线程。这对于通用对象来说非常有用,在这些对象中,任何可能继续的给定执行流程都不关心它实际使用的是哪个对象。或者您确定执行流程保留在给定线程中。

但除此之外,没有。您不希望在同时需要执行流程始终使用同一个对象的情况下使用 ThreadStatic,但您不能保证执行流程将保持在同一个线程上。(对不起,如果最后一个陈述似乎很明显......我只是想确保它很清楚)。

于 2014-10-22T20:50:31.557 回答
1

假设await是从异步友好的调用堆栈(异步控制器/处理程序)调用的,那么,是的,断言保证成功。

ASP.NET SynchronizationContext 将处理使 HttpContext 在返回线程上可用(除非您使用ConfigureAwait(false))。但是,这通常不适用于线程数据,因此如果您具有这种类型的“请求全局”状态,则更喜欢 HttpContext.Items。

于 2014-10-22T21:04:29.950 回答