首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >ASP.NET内核中AsyncLocal的安全性

ASP.NET内核中AsyncLocal的安全性
EN

Stack Overflow用户
提问于 2016-04-09 08:11:39
回答 2查看 15.4K关注 0票数 61

对于.NET核心,AsyncLocalCallContext的替代品。然而,目前还不清楚在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核心。我想使用以下中间件:

代码语言:javascript
复制
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构造时,我就不会意外地构建不稳定的代码。

EN

回答 2

Stack Overflow用户

发布于 2016-05-01 23:53:48

我正在查看CoreClr的ExecutionContext类的源代码:https://github.com/dotnet/coreclr/blob/775003a4c72f0acc37eab84628fcef541533ba4e/src/mscorlib/src/System/Threading/ExecutionContext.cs

根据我对代码的理解,异步本地值是每个ExecutionContext实例的字段/变量。它们不是基于ThreadLocal或任何特定于线程的持久化数据存储。

为了验证这一点,在我对线程池线程的测试中,当相同的线程池线程被重用时,留在异步本地值中的实例是不可访问的,并且“左”实例用于清理自身的析构函数在下一个GC周期中被调用,这意味着该实例如预期的那样是GCed的。

票数 16
EN

Stack Overflow用户

发布于 2020-04-09 20:29:29

如果有人在ASP.NET经典(非核心)应用程序中搜索AsyncLocal是否“安全”后(就像我一样),添加我的两点意见(一些评论者一直在问这个问题,我也看到一个删除的答案询问同样的问题)。

我写了一个模拟ASP.Net的ThreadPool行为的小测试

  1. AsyncLocal 总是在请求之间被清除,即使线程池重新使用了现有的线程。因此,在这一点上是“安全的”,不会将数据泄漏到另一个控制器,即使在相同的上下文中(例如,在global.asax中运行的代码和在thread.
  2. However,中运行的代码之间)也可以清除AsyncLocal。由于MVC方法有时运行在与非MVC代码不同的线程上,请参见以下问题,例如:asp.net mvc 4, thread changed by model binding?
  3. Using ThreadLocal不安全b/c它在线程池中的线程被重用后保留该值。切勿在web应用程序中使用ThreadLocal。我知道问题不是关于ThreadLocal的,我只是向考虑使用它的人添加这个警告,对不起。

在ASP.NET MVC 5 .NET 4.7.2下测试。

总体而言,如果不能直接访问HttpContext.Current中的短时间缓存内容,AsyncLocal似乎是一种完美的替代方案。不过,您最终可能会更频繁地重新计算缓存值,但这不是一个大问题。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36511243

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档