【问题标题】:Access HttpContext.Current from different threads从不同的线程访问 HttpContext.Current
【发布时间】:2012-02-14 01:31:06
【问题描述】:

我有一个 C# ASP.NET 应用程序,它启动大约 25 个不同的线程,运行一个名为 SiteCrawler.cs 的类中的一些方法。

HttpContext.Current.Session 中,我想保存用户的搜索结果,并在所有线程完成运行后呈现给用户。我的问题是HttpContext.Current 对象在生成的线程中为空,因为它在那里不存在。

由于应用程序是多线程时的限制,我还有哪些其他选项可以在不使用会话的情况下保存用户/会话特定数据?

我试图搜索 Stackoverflow 的每一寸以找到解决方案,但没有任何运气......

【问题讨论】:

  • 我猜你总是可以在创建新线程并更新其中的会话时传递当前的 HttpContext
  • 不是可以运行25个线程,在asp.net线程中收集结果,然后将完整的结果存储在你的会话中吗?
  • 根据经验,为“长时间运行的进程”生成线程在面向 Web 的应用程序中是个坏主意。
  • musefan - 我想过,但我不确定这是否是个好主意,因为生成的线程会改变传递的 HttpContext 的内容,到那时也会有所不同。
  • Wouter de Kort - 您将如何实现这一目标?

标签: c# asp.net session httpcontext


【解决方案1】:

看看 Fritz Onion 的这篇文章:Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code。挺长的,不过你的要求也不算太琐碎。

K. Scott Allen 也发表了一篇关于这个问题的简短文章:Working With HttpContext.Current

【讨论】:

  • 那篇文章“使用 HttpContext.Current”对于帮助我解决我的特殊问题至关重要,谢谢。
【解决方案2】:

@Rory 在上面发表了评论,HttpContext 中的某些对象即使您将其传递到线程中也会变为 null。 User 属性发生在我身上。因此,您可以像这样将 User 复制到 CurrentPrincipal 线程:

在控制器上下文中,保存用户:

            _user = HttpContext.Current.User;
            var processThread = new Thread(() => ThreadedCode());
            processThread.Start();

在线程中,设置“线程的”用户:

    private static void ThreadedCode()
    {
        // Workaround for HttpContext.Current.User being null.
        // Needed for CreatedBy and RevisedBy.
        Thread.CurrentPrincipal = _user;

请注意,HttpContext 仅在请求的生命周期内可用。线程的生存时间可能比请求长得多,这可能就是您首先需要线程的原因! :)

【讨论】:

  • @Rory 感谢您在上面的评论。这让我想到了这个替代答案。
【解决方案3】:

只需将 HttpContext.Current 添加到您的类 SiteCrawler.cs 的构造函数中

public class SiteCrawler
{
     HttpContext context = HttpContext.Current;

    public void Method()
    {
        context.WhateverYouWant
    }
}

【讨论】:

    【解决方案4】:

    在我的应用程序中有很多使用HttpContext.Current 的代码,我无法修改该代码。

    worker.DoWork() 下面的示例使用该代码。而且我必须在单独的线程中运行它。

    我想到了以下解决方案:

     HttpContext ctx = HttpContext.Current;
     Thread t = new Thread(new ThreadStart(() =>
                    {
                        HttpContext.Current = ctx;
                        worker.DoWork();
                    }));
     t.Start();
     // [... do other job ...]
     t.Join();
    

    【讨论】:

    • 哇,谢谢你救了我!我想知道如果我更多地使用它会如何影响记忆
    • 请注意,通常这是一件坏事。例如,有时您可能会发现 HttpContext.Current.Items[x] 返回 null 而不是您放在那里的值,因为 ASP 在 http 请求完成后清理了资源。这是一种快速而肮脏的方法,可能在大多数情况下都有效,但在任何重要的事情上都不要依赖它。
    • 我不同意@Rory 的观点。当您将其与其他选项进行比较时,我认为这是迄今为止最优雅的解决方案。首先,您不是在克隆 HttpContext 而是只是传递一个引用。由于 Thread 运行时间可能比 HttpRequest 本身更长,因此可以在访问之前轻松检查 HttpContext 是否为空。作为对上述代码 sn-p 传递“ctx”的改进,也可以通过 WeakReference 完成 - 这样线程就不会在需要时阻止 HttpContext 被 GC'ed。再次根据您需要的生命周期调整上面的代码以满足您的需求。 + 接受的答案
    • @Rory 暗示了一个即发即弃的异步调用,而 that 是个坏主意。如果你不做任何时髦的事情并确保网络请求等待所有异步调用完成,你应该没问题,对吧?
    • 我想我在写之前的评论时没有注意到这个答案上的t.Join() 调用。我同意如果后台线程在 http 请求之前完成,应该没问题。根据我的经验,当 Web 请求首先完成并且后台任务仍在运行时,就会出现问题。请注意,问题不在于 HttpContext 被 gc-ed - 显然仍然有一个引用,所以它不会 - 而是 ASP.NET 中的某些内容删除了HttpContext.Current 中的所有元素,所以如果你希望有任何东西在里面那个系列你运气不好。
    【解决方案5】:

    您可以将其保存到数据库中,然后,您可以让用户的浏览器不断刷新或使用 ajax 或使用新的信号器检查结果是否已写入 db。希望有帮助。

    【讨论】:

    • 将会话变量保存到数据库不会有问题。我只是不知道将它绑定到正确的会话并在线程完成时检索它的最佳方法是什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多