对于.NET核心,AsyncLocal
是CallContext
的替代品。然而,目前还不清楚在ASP.NET核心中使用它有多“安全”。
在ASP.NET 4(MVC5)和更早的版本中,thread-agility model of ASP.NET made CallContext
unstable。因此,在ASP.NET中,实现每个请求的逻辑上下文行为的唯一安全方法就是使用HttpContext.Current.Items。在幕后,HttpContext.Current.Items是用CallContext
实现的,但它是以一种对ASP.NET安全的方式完成的。
相比之下,在OWIN/Katana Web API的上下文中,线程敏捷性模型不是问题。在careful considerations of how correctly to dispose it之后,我能够安全地使用CallContext。
但现在我要处理的是ASP.NET核心。我想使用以下中间件:
public class MultiTenancyMiddleware
{
private readonly RequestDelegate next;
static int random;
private static AsyncLocal<string> tenant = new AsyncLocal<string>();
//This is the new form of "CallContext".
public static AsyncLocal<string> Tenant
{
get { return tenant; }
private set { tenant = value; }
}
//This is the new verion of [ThreadStatic].
public static ThreadLocal<string> LocalTenant;
public MultiTenancyMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
//Just some garbage test value...
Tenant.Value = context.Request.Path + random++;
await next.Invoke(context);
//using (LocalTenant = new AsyncLocal<string>()) {
// Tenant.Value = context.Request.Path + random++;
// await next.Invoke(context);
//}
}
}
到目前为止,上面的代码似乎工作得很好。但至少有一个危险信号。在过去,确保将CallContext
视为必须在每次调用后释放的资源是至关重要的。
现在我看到没有不言而喻的方法来“清理”AsyncLocal
。
我加入了代码,注释掉了,展示了ThreadLocal<T>
是如何工作的。它是IDisposable
,因此它有一个明显的清理机制。相反,AsyncLocal
不是IDisposable
。这是令人不安的。
这是因为AsyncLocal
还没有处于发布候选状态吗?或者这是因为真的不再需要执行清理?
即使我在上面的例子中正确地使用了AsyncLocal
,ASP.NET核心中是否有任何老式的“线程敏捷性”问题会使这个中间件无法工作?
特别说明
对于那些不熟悉CallContext
在ASP.NET应用程序中存在的问题的人,在this SO post, Jon Skeet中引用了一个in-depth discussion about the problem (而后者又引用了commentary from Scott Hanselman)。这个“问题”不是bug --它只是一个必须仔细考虑的情况。
此外,我可以亲自证明这种不幸的行为。在构建ASP.NET应用程序时,我通常会将负载测试作为自动化测试基础结构的一部分。在负载测试期间,我可以看到CallContext
变得不稳定(其中可能有2%到4%的请求显示CallContext
被破坏)。我也见过Web API GET
具有稳定的CallContext
行为,但POST
操作都不稳定的情况。实现完全稳定性的唯一方法是依靠HttpContext.Current.Items。
然而,在ASP.NET核心的情况下,我不能依靠HttpContext.Items...there不是这样的静态访问点。我还不能为我正在修补的.NET核心应用程序创建负载测试,这也是我没有回答这个问题的部分原因。:)
:请理解我所讨论的“不稳定性”和“问题”根本不是bug。CallContext并没有什么缺陷。这个问题只是ASP.NET采用的线程分派模型的结果,解决方法很简单,就是知道问题的存在,并进行相应的编码(例如,在ASP.NET应用程序中使用
HttpContext.Current.Items
而不是CallContext
)。
我提出这个问题的目的是理解这种动态如何在ASP.NET核心中应用(或不应用),这样在使用新的AsyncLocal
构造时,我就不会意外地构建不稳定的代码。
发布于 2016-05-01 15:53:48
我正在查看CoreClr的ExecutionContext类的源代码:https://github.com/dotnet/coreclr/blob/775003a4c72f0acc37eab84628fcef541533ba4e/src/mscorlib/src/System/Threading/ExecutionContext.cs
根据我对代码的理解,异步本地值是每个ExecutionContext实例的字段/变量。它们不是基于ThreadLocal或任何特定于线程的持久化数据存储。
为了验证这一点,在我对线程池线程的测试中,当相同的线程池线程被重用时,留在异步本地值中的实例是不可访问的,并且“左”实例用于清理自身的析构函数在下一个GC周期中被调用,这意味着该实例如预期的那样是GCed的。
发布于 2020-04-09 12:29:29
如果有人在ASP.NET经典(非核心)应用程序中搜索AsyncLocal
是否“安全”后(就像我一样),添加我的两点意见(一些评论者一直在问这个问题,我也看到一个删除的答案询问同样的问题)。
我写了一个模拟ASP.Net的ThreadPool行为的小测试
AsyncLocal
总是在请求之间被清除,即使线程池重新使用了现有的线程。因此,在这一点上是“安全的”,不会将数据泄漏到另一个控制器,即使在相同的上下文中(例如,在global.asax中运行的代码和在thread.AsyncLocal
。由于MVC方法有时运行在与非MVC代码不同的线程上,请参见以下问题,例如:asp.net mvc 4, thread changed by model binding?ThreadLocal
不安全b/c它在线程池中的线程被重用后保留该值。切勿在web应用程序中使用ThreadLocal。我知道问题不是关于ThreadLocal的,我只是向考虑使用它的人添加这个警告,对不起。在ASP.NET MVC 5 .NET 4.7.2下测试。
总体而言,如果不能直接访问HttpContext.Current
中的短时间缓存内容,AsyncLocal
似乎是一种完美的替代方案。不过,您最终可能会更频繁地重新计算缓存值,但这不是一个大问题。
https://stackoverflow.com/questions/36511243
复制相似问题