0

我正在创建一个用于 Intranet 的文件处理器。我在另一个问题中描述了它 - ERR_EMPTY_RESPONSE 使用 C# 在 ASP.Net 中处理大量文件时

现在,正如上述问题的答案所建议的,我正在尝试使用线程来执行文件处理任务。

但有一个问题。我需要新创建的线程将反馈写入页面中的组件(asp:panel 或 div 或其他)。这些反馈将是几个数据库操作的结果。

应用程序读取这些 txt,解释其中的每一行,并将数据插入数据库。插入数据库的每一行都必须返回一个反馈,例如“成功插入注册表 'regname'”或“我在文件 'filename' 中插入注册表 '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 调用,并在加载时用一行数字填充名为“pnlConfirms”的 asp:panel。

我将其更改为:

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:panels - 比如插入错误和警告。

我的代码已经在try...catch语句下有这些反馈,但是它们没有从线程池中获取任何 asp:panel 的输出(当直接从DoImport()函数调用时,它可以工作,就像我发布的第一个示例一样)。

我做错了什么,但我不知道是什么(我研究了将近 2 周)。请帮忙!

4

2 回答 2

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可用于为您的客户端创建一个 javascript Web 服务代理(其他有趣的文章),并且是其余控件所必需的

  • 一些javascript(或jquery)可用于更新客户端的“进程运行/进程完成提示”。即当对Web服务的调用结束时,您可以使用javascript使用DOM来更新页面,或者加载一个新页面或带有特殊参数的同一页面以显示该过程的结果

这样你就可以做你想做的事:

1) 以显示进程正在运行的状态显示页面

2) 以显示进程结束的状态显示相同或其他页面

诀窍是使浏览器与服务器通信,而这只能使用一些可用的 ajax 技术来完成。

另一种典型的技术是使用 jQuery.ajax,就像encosia.com 中解释的那样

根据 OP 消息,所有文件的处理过程都会非常缓慢,以至于它会占用 Web 服务调用。如果是这种情况,您可以使用以下解决方案:

1)创建一个处理一个(或一批)待处理文件的Web服务,并在完成当前文件(或批处理)的处理时至少返回待处理文件的数量。

2)从客户端(javascript)调用网络服务。完成后,更新显示待处理文件数量的页面,如果此数量大于零,则再次调用 Web 服务。

3)当调用web服务返回0个挂起文件时,可以更新页面显示工作完成,不要再调用了。

如果一次处理所有文件,客户端不会有任何反馈,也会出现超时。此外,IIS 可以决定停止正在工作的工作线程。IIS 这样做有几个原因。

一个更可靠但更难实施的解决方案是:

1) 实现一个 Windows 服务,它执行文件处理

2) 实现一个返回待处理文件数量的 Web 服务(您可以使用文件系统、数据库表或类似的东西间接通信 Windows 服务和 Web 应用程序)

3) 使用网页中的计时器(ajax 计时器或 javascript setInterval)使用 Web 服务每 N 秒轮询一次服务器,直到待处理文件的数量为 0。

更难的方法是在 Windows 服务中托管 WCF 服务,而不是在 Web 应用程序和 Windows 服务之间进行间接通信。这种情况要复杂得多,因为您需要使用线程来完成工作,并参加对 wcf 服务的调用。如果您可以使用间接通信,则实施起来会容易得多。dtabse 表是一个简单而有效的解决方案:您的工作进程在处理文件时更新表中的一行,并且 Web 服务从该表中读取进度状态。

对于一个不那么简单的问题,有许多不同的想法。

于 2012-07-05T22:38:35.343 回答
0

您正在启动新线程(或更准确地说,在线程池中的一个空闲线程上运行您的代码)而不是等待主线程中的结果。如果您想走这条路,则需要使用诸如 Thread.Join (如果您将使用手动线程创建)或其他同步机制作为事件。

您链接到的问题建议使用您没有使用的异步页面。您将开始处理请求,启动任务并释放线程,当任务完成时您完成请求。

旁注:考虑简单地在处理请求的主线程上进行所有转换。除非您期望缓慢的 I/O 完成将 CPU 工作从一个线程转移到另一个线程的任务,否则可能不会产生显着的收益。请测量您当前解决方案的性能并确认它不符合您为应用程序设置的性能目标。(如果您出于娱乐/教育目的这样做,则不适用)。

于 2012-07-05T21:44:59.947 回答