多线程时如何使用HttpContext.Current?

时间:2014-08-06 15:18:35

标签: c# asp.net multithreading httpcontext

为了澄清我的问题,我一直在开发一个应用程序,它根据用户的输入(使用excel电子表格)执行大量数据库更新/ Web服务调用。如果有很多更新要使该过程运行超过20分钟。

为了阻止我的用户界面冻结/超时,我一直在研究多线程,这样我就可以以异步方式运行我的长时间运行过程,同时只需在进程运行时显示动画gif。

这一切似乎与我的测试数据一起很好地运行,但是当我在实际的长时间运行过程中替换时,我得到关于HttpContext.Current.User.Identity.Name的错误。我已经对此进行了阅读,并从此article1我认为,如果您设置了“异步”,财产到真实'在page指令中使用RegisterAsyncTask方法然后可以访问HttpContext.Current。然而,对我而言,这似乎并非如此。我确定这是我正在做的事情,所以这是我的代码(我主要使用以下文章来撰写此article2article3 ):

ASP.NET页面

<%@ Page Title="Home Page" Async="true" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.cs" Inherits="MyApp.Index" %>

C# - RegisterAsyncTask是在点击按钮时完成的,它启动了长时间运行的过程:

protected void ProcessUpdates()
{
    //Register async task to allow the processing of valid updates to occurr in the background
    PageAsyncTask task = new PageAsyncTask(OnBegin, OnEnd, OnTimeOut, null);
    RegisterAsyncTask(task);

}

IAsyncResult OnBegin(Object sender, EventArgs e, AsyncCallback cb, object state)
{
    return Worker.BeginWork(cb, state);
}

private void OnEnd(IAsyncResult asyncResult)
{
    //UpdateResults list should now have been filled and can be used to fill the datagrid
    dgProcessedUpdates.DataSource = Worker.UpdateResults;
    dgProcessedUpdates.CurrentPageIndex = 0;
    dgProcessedUpdates.DataBind();

    lblProgress.Text = "Update Results: update success / failure is shown below";
}

private void OnTimeOut(IAsyncResult asyncResult)
{
    lblProgress.Text = "The process has timed out. Please check if any of the updates have been processed.";
}

C# - 工人阶级

public class Worker
{
    public static List<AuditResult> UpdateResults = new List<AuditResult>();
    private delegate void del();

    //This method is called when the thread is started
    public static IAsyncResult BeginWork(AsyncCallback cb, object state)
    {
        del processing = DoUpdateProcessing;
        return processing.BeginInvoke(cb, state);
    }

    private static void DoUpdateProcessing()
    {
        //UpdateResults = ExcelFileProcessing.PassValidUpdates();

        //Testing

        Thread.Sleep(5000);

        int i = 0;

        while(i < 10)
        {
            AuditResult ar = new AuditResult();
            ar.Result = "Successful";
            ar.JobNumber = (1000 + i).ToString();
            ar.NewValue = "Test New Value " + i.ToString();
            ar.ResultDate = DateTime.Now.ToString();
            ar.UserName = HttpContext.Current.User.Identity.Name;

            UpdateResults.Add(ar);
            i++;
        }
    }
}

最初我的测试代码没有包含对ar.UserName的HttpContext.Current.User.Name的调用,但是在我回过头来调用ExcelFileProcessing.PassValidUpdates()之后,我决定这样做。当我到达那个部分(ar.UserName = HttpContext.Current.User.Identity.Name)时,它表示&#39;对象引用没有设置为对象的实例&#39;,这表明HttpContext没有被携带跨越第二个主题。我怎么能这样做?

更新

我目前还原回到我以前的代码(原来并没有工作),只是将HttpContext.Current作为变量传递给我的DoWork方法,按照SO question这样:

创建第二个帖子

    protected void ProcessValidUpdates()
    {
        Worker workerObject = new Worker();
        HttpContext ctx = HttpContext.Current;
        Thread workerThread = new Thread(new ThreadStart(() =>
                                {
                                    HttpContext.Current = ctx;
                                    workerObject.DoWork();
                                }));

        workerThread.Start();

        //Loop until worker thread activates
        while (!workerThread.IsAlive) ;

        //Put main thread to sleep to allow the worker thread to do some work
        Thread.Sleep(1000);

        //Request the worker thread stop itself
        workerObject.RequestStop();

        //Use the Join method to block the current thread until the object's thread terminates
        workerThread.Join();

        //UpdateResults list should now have been filled and can be used to fill the datagrid
        dgProcessedUpdates.DataSource = Worker.UpdateResults;
        dgProcessedUpdates.CurrentPageIndex = 0;
        dgProcessedUpdates.DataBind();

        lblProgress.Text = "Update Results: update success / failure is shown below";
    }

工人阶级

public class Worker
{
    //volatile hints to the compiler that this data member will be accessed by multiple threads.
    private volatile bool _shouldStop;
    public static List<AuditResult> UpdateResults = new List<AuditResult>();

    //This method is called when the thread is started
    public void DoWork()
    {
        while (!_shouldStop)
        {
            //Testing
            Thread.Sleep(5000);

            int i = 0;

            while (i < 10)
            {
                AuditResult ar = new AuditResult();
                ar.Result = "Successful";
                ar.JobNumber = (1000 + i).ToString();
                ar.NewValue = "Test New Value " + i.ToString();
                ar.ResultDate = DateTime.Now.ToString();
                ar.UserName = HttpContext.Current.User.Identity.Name;

                UpdateResults.Add(ar);
                i++;
            }

        }
    }

    public void RequestStop()
    {
        _shouldStop = true;
    }
}

这似乎有效,我现在可以访问HttpContext.Current和我期望的用户名。我认为这可能在某种程度上是你们有些人提出的建议。我很欣赏Andrew Morton建议的解决方案,但目前需要进行重大改写。目前,我的进程已经调用Web服务来执行数据库操作并返回成功或失败结果。它还必须直接调用另一个BPEL服务。因此我怀疑如果我不得不将所有这些包装到另一个Web服务中,可能会有进一步的性能命中。此外,大多数对该过程的调用都不会长时间运行(可能少于10分钟),因此这只是为了解决超过20分钟的少数请求。最后,这可能仅由1或2人使用,因此一次不可能有大量请求。

然而,考虑到我目前的解决方案,有什么我应该知道的可能会让我失望吗? IIS导致问题?任何额外的帮助将非常感谢。

1 个答案:

答案 0 :(得分:2)

我在共享服务器上有一个站点。我需要有一个BATCH工作,我在另一个线程中这样做。它可以运行长达1小时(我ping网站,因此工作进程不会停止)。

我走上了捆绑的道路,以获得当前的背景。经过数小时的研究和搜索,无法完成。在新的线程中,httpcontent.current不存在,它与用户访问的线程不同,因此上下文没有延续,并且您无法访问登录用户,因为他们没有登录到该线程。