我正在创建一个用于Intranet的文件处理器。 我在另一个问题中描述了它 - ERR_EMPTY_RESPONSE when processing a large number of files in ASP.Net using C#
现在,正如上面问题的答案所示,我试图使用线程来执行文件处理任务。
但是有一个问题。我需要新创建的线程将反馈写入页面中的组件(asp:panel,或div,或其他)。这些反馈将来自几个数据库操作。
应用程序读取这些txts,解释它的每一行,并在数据库中插入数据。插入数据库中的每一行都必须返回反馈,例如"注册表' regname'插入成功",或者"我在插入注册表' regname'时遇到问题在文件'文件名'中,跳到下一个注册表"。
我做了一些非常简单的测试:
protected void DoImport()
{
try
{
MainBody.Style.Add(HtmlTextWriterStyle.Cursor, "wait");
int x = 0;
while (x < 10000)
{
ReturnMessage(String.Format("Number {0}<hr />", x), ref pnlConfirms);
x++;
}
}
catch (Exception ex)
{
ReturnMessage(String.Format("<font style='color:red;'><b>FATAL ERROR DURING DATA IMPORT</b></font><br /><br /><font style='color:black;'><b>Message:</b></font><font style='color:orange;'> {0}</font><br />{1}", ex.Message, ex.StackTrace), ref pnlErrors);
}
finally
{
MainBody.Style.Add(HtmlTextWriterStyle.Cursor, "default");
}
}
此函数从Page_Load调用,并填充一个名为&#34; pnlConfirms&#34;的asp:面板。有一行数字,但一次性加载。
我把它改为:
protected void DoImport()
{
try
{
MainBody.Style.Add(HtmlTextWriterStyle.Cursor, "wait");
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
}
catch (Exception ex)
{
ReturnMessage(String.Format("<font style='color:red;'><b>FATAL ERROR DURING DATA IMPORT</b></font><br /><br /><font style='color:black;'><b>Message:</b></font><font style='color:orange;'> {0}</font><br />{1}", ex.Message, ex.StackTrace), ref pnlErrors);
}
finally
{
MainBody.Style.Add(HtmlTextWriterStyle.Cursor, "default");
}
}
private void DoWork(Object stateInfo)
{
int x = 0;
while (x < 10000)
{
ReturnMessage(String.Format("Number {0}<hr />", x), ref pnlConfirms);
x++;
}
}
两者都使用此功能:
public void ReturnMessage(string message, ref Panel panel, bool reset = false)
{
if (reset)
{
panel.Controls.Clear();
}
Label msg = new Label();
msg.Attributes.Add("width", "100%");
msg.Text = message;
panel.Controls.Add(msg);
}
我需要ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
来填充那些带有反馈的asp:面板 - 比如插入错误和警告。
我的代码已经在try...catch
语句下有了这些反馈,但是它们没有从threadpool输出到任何asp:面板(直接从DoImport()
函数调用时它可以工作,就像在我发布的第一个例子。)
我做了一件非常错误的事,但我无法找到答案(而且我将近两周研究这件事)。请帮助!
答案 0 :(得分:1)
在ASP.NET中,当浏览器请求页面时,该页面会在处理完成后立即呈现并发送到浏览器,因此浏览器将显示最终呈现的页面。
根据您的代码,您尝试渲染页面,显示等待光标,并期望它显示在浏览器上,然后,光标将被默认光标更改。正如我所解释的,独立于使用或不使用其他线程,在完全呈现之前,页面不会被发送到浏览器。所以你永远不会在客户端看到等待光标。
最简单的等待你要做的就是使用Web服务(传统的.asmx或WCF)和AJAX(jquery os ASP.NET AJAX)。
1)创建一个执行处理的Web服务
2)创建一个发送到浏览器的页面,并使用javascript(jQuery或ASP.NET AJAX)调用Web服务,并显示一些内容让用户知道正在处理请求。 (等待光标,甚至更好的GIF动画)
3)当流程结束时,您的javascript将从Web服务获得响应,您可以更新页面以让用户知道流程已完成。
如果您没有javascript经验,可以使用以下方法完成大部分任务:
ScriptManager可用于create a javascript web service proxy用于您的客户端(other interesting article),并且是其他控件所必需的
一些javascript(或jquery),可用于更新客户端的“进程运行/处理完成提示”。即当对Web服务的调用结束时,您可以使用javascript使用DOM更新页面,或者使用特殊参数加载新页面或同一页面以显示该过程的结果
通过这种方式,你可以做你想做的事:
1)显示处于显示进程正在运行的状态的页面
2)在显示流程结束的状态下显示相同或其他页面
诀窍是将浏览器与服务器通信,这只能通过一些可用的ajax技术来完成。
另一种典型技术是使用jQuery.ajax,如explained in encosia.com
根据OP消息,所有文件的进程都会很慢,以至于它会绑定Web服务调用。如果是这种情况,您可以使用此解决方案:
1)创建一个处理一个(或一批)待处理文件的Web服务,并在完成当前文件(或批处理)的处理时至少返回待处理文件的数量。
2)从客户端(javascript),调用Web服务。完成后,更新显示待处理文件数的页面,如果此数字大于零,则再次调用Web服务。
3)当对Web服务的调用返回0个待处理文件时,您可以更新页面以显示工作已完成,并且不再调用它。
如果您一次处理所有文件,客户端就没有反馈,也会有超时。此外,IIS可以决定停止正在进行工作的工作线程。 IIS出于几个原因这样做。
更可靠的解决方案,但更难实施,是:
1)实现执行文件处理的Windows服务
2)实现一个Web服务,它返回挂起文件的数量(您可以使用文件系统,数据库表或类似的东西间接地传递Windows服务和Web App)
3)使用网页中的计时器(ajax计时器或javascript setInterval),每隔N秒使用Web服务轮询服务器,直到待处理文件的数量为0。
更难的方法是在Windows服务中托管WCF服务,而不是在Web应用程序和Windows服务之间进行间接通信。这种情况要复杂得多,因为您需要使用线程来完成工作,并参加对wcf服务的调用。如果你可以使用间接通信,那么实现起来会容易得多。 dtabse表是一个简单而有效的解决方案:您的工作进程在处理文件时更新表的一行,并且Web服务从此表中读取进度状态。
对于一个不那么简单的问题,有许多不同的灵魂。
答案 1 :(得分:0)
您正在开始新线程(或更精确地在线程池中的一个空闲线程上运行您的代码)而不是在主线程中等待结果。类似于Thread.Join(如果您将使用手动线程创建)或其他同步机制,因为如果您想要使用此路径,则需要使用事件。
您链接的问题建议使用您不会执行的异步页面。您将开始处理请求,启动任务并释放线程,当任务完成后,您完成请求。
附注:考虑简单地在处理请求的主线程上进行所有转换。除非您期望慢速I / O完成任务,否则将CPU工作从一个线程移动到另一个线程可能不会产生显着的增益。请测量当前解决方案的性能,并确认它不符合您为应用程序设置的性能目标。 (如果您出于娱乐/教育目的而这样做,则不适用。)