在异步操作中初始化文化已完成

发布于 2024-12-28 09:41:23 字数 1950 浏览 3 评论 0原文

我正在使用 ASP.NET MVC 3。 一旦我决定将一些同步操作更改为异步操作。在此之前,我创建了一个示例异步操作来测试它是否可以正常工作。事情是这样的:

    public void SampleAsync()
    {
        AsyncManager.OutstandingOperations.Increment();
        var task = System.Threading.Tasks.Task.Factory.StartNew(() => DoStuff());
        task.ContinueWith(t =>
        {
            AsyncManager.Parameters["pars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    private object DoStuff()
    {
        System.Threading.Thread.Sleep(20000);
        return null;
    }

    public ActionResult SampleCompleted(object pars)
    {
        return View();
    }

但是这个动作并不是异步进行的!这是绝对同步的。至少,它作为传统操作阻止其他请求(在运行时)。经过几个小时的调查,我发现问题出在我初始化当前文化的 asax 中:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        CultureInfo ci;
        //It's important to check whether session object is ready
        if (HttpContext.Current.Session != null)
        {
            ci = (CultureInfo)this.Session["Culture"];
            //Checking first if there is no value in session 
            //and set default language 
            //this can happen for first user's request

            if (ci == null)
            {
                ci = GetCultureFromCookie();
                this.Session["Culture"] = ci;
            }
            if (ci == null)
            {
                ci = GetStandardCulture();
                this.Session["Culture"] = ci;
            }
        }
        else
        {
            ci = GetCultureFromCookie();
            if (ci == null) ci = GetStandardCulture();
        }
        //Finally setting culture for each request
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
    }

当我注释掉这个方法时,一切都开始正常工作。我认为问题与从操作完成线程访问 HttpContext.Current 有关。但在这种情况下,我应该如何初始化用户当前的文化呢?

I am using ASP.NET MVC 3.
Once I've decided to change some of my synchronous actions to asynchronous ones. Before doing that I created a sample asynchronous action to test whether it will work properly or not. It was something like this:

    public void SampleAsync()
    {
        AsyncManager.OutstandingOperations.Increment();
        var task = System.Threading.Tasks.Task.Factory.StartNew(() => DoStuff());
        task.ContinueWith(t =>
        {
            AsyncManager.Parameters["pars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    private object DoStuff()
    {
        System.Threading.Thread.Sleep(20000);
        return null;
    }

    public ActionResult SampleCompleted(object pars)
    {
        return View();
    }

But this action didn't work asynchronously! It was absolutely synchronous. At least, it was blocking other requests (while running) as a traditional action. After a few hours of investigation I've found out that the problem was in my asax where I was initializing the current culture:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        CultureInfo ci;
        //It's important to check whether session object is ready
        if (HttpContext.Current.Session != null)
        {
            ci = (CultureInfo)this.Session["Culture"];
            //Checking first if there is no value in session 
            //and set default language 
            //this can happen for first user's request

            if (ci == null)
            {
                ci = GetCultureFromCookie();
                this.Session["Culture"] = ci;
            }
            if (ci == null)
            {
                ci = GetStandardCulture();
                this.Session["Culture"] = ci;
            }
        }
        else
        {
            ci = GetCultureFromCookie();
            if (ci == null) ci = GetStandardCulture();
        }
        //Finally setting culture for each request
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
    }

When I commented out this method, everything started to work just fine. I assume that the problem is related with accessing the HttpContext.Current from an action complete thread. But in this case, how should I init the current culture of the user?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

蓝眼睛不忧郁 2025-01-04 09:41:23

以下行是问题所在:

this.Session["Culture"] = ci;

您正在写入会话。在 ASP.NET 中,会话不是线程安全的。因此,ASP.NET 会同步访问。这基本上意味着,如果您点击一个 ASP.NET 处理程序,该处理程序从同一会话的 2 个并行请求写入会话,则 ASP.NET 将简单地按顺序执行它们。

别误会我的意思。这并不意味着来自 2 个不同用户的两个并行请求将按顺序执行。它们将完全平行。只是我假设您对此控制器操作运行了 2 个并行 AJAX 请求,并且您观察到了 FireBug 或其他内容中的行为。由于 2 个 AJAX 请求来自同一会话,并且由于您写入会话,因此您的请求无法并行执行。

你必须找到另一种方式来处理文化。 Cookie 或 url 参数(在 SEO 方面更好)作为一个不错的替代方案浮现在脑海中。而且由于会话无论如何都是坏事,因此摆脱它们只会对您的应用程序有利。

在 ASP.NET MVC 3 中,您可以使用 SessionState 属性来指示控制器操作将不使用会话或仅从中读取:

[SessionState(SessionStateBehavior.ReadOnly)]
public class FooController: AsyncController
{

}

现在您可以并行处理来自同一会话的请求。显然,每次尝试写入会话都会引发异常。

The following line is the problem:

this.Session["Culture"] = ci;

You are writing to the session. In ASP.NET the session is not thread safe. For this reason ASP.NET synchronizes access to it. This basically means that if you hit an ASP.NET handler that does writes to the session from 2 parallel requests from the same session, ASP.NET will simply execute them sequentially.

Don't get me wrong. This doesn't mean that two parallel requests from 2 different users will be executed sequentially. They will be perfectly parallel. It's just that I suppose you ran 2 parallel AJAX requests to this controller action and you observed the behavior in FireBug or whatever. Since the 2 AJAX requests were from the same session and since you write to the session, you are busted, your requests cannot be executed in parallel.

You will have to find another way to handle culture. Cookies or url parameters (better in terms of SEO) spring to mind as a nice alternative. And since sessions are bad thing anyways, getting rid of them would only be beneficial to your application.

In ASP.NET MVC 3 you could use the SessionState attribute to indicate that a controller action will either not use session or it will be only reading from it:

[SessionState(SessionStateBehavior.ReadOnly)]
public class FooController: AsyncController
{

}

Now you can have parallel processing of requests from the same session. Obviously every attempt to write to the session will throw an exception.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文