为了澄清我的问题,我一直在开发一个应用程序,它根据用户的输入(使用excel电子表格)执行大量数据库更新/ Web服务调用。如果有很多更新要使该过程运行超过20分钟。
为了阻止我的用户界面冻结/超时,我一直在研究多线程,这样我就可以以异步方式运行我的长时间运行过程,同时只需在进程运行时显示动画gif。
这一切似乎与我的测试数据一起很好地运行,但是当我在实际的长时间运行过程中替换时,我得到关于HttpContext.Current.User.Identity.Name的错误。我已经对此进行了阅读,并从此article1我认为,如果您设置了“异步”,财产到真实'在page指令中使用RegisterAsyncTask方法然后可以访问HttpContext.Current。然而,对我而言,这似乎并非如此。我确定这是我正在做的事情,所以这是我的代码(我主要使用以下文章来撰写此article2和article3 ):
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导致问题?任何额外的帮助将非常感谢。
答案 0 :(得分:2)
我在共享服务器上有一个站点。我需要有一个BATCH工作,我在另一个线程中这样做。它可以运行长达1小时(我ping网站,因此工作进程不会停止)。
我走上了捆绑的道路,以获得当前的背景。经过数小时的研究和搜索,无法完成。在新的线程中,httpcontent.current不存在,它与用户访问的线程不同,因此上下文没有延续,并且您无法访问登录用户,因为他们没有登录到该线程。