线程在ASP中创建一个文件

时间:2015-08-21 22:24:43

标签: asp.net vb.net multithreading excel

我正在尝试使用一个可能有60000条记录的表在Excel中创建一个报表,所以我已经有了这样做的功能并且它可以工作,问题是当表实际上有这么多的记录时它也会持续很多,应用程序死了。我认为解决方案是将该函数放在一个Thread中并运行它后台,但似乎Thread开始执行然后它就停止了。我是这样做的:

首先我创建一个txt文件并检查它是否已创建,如果是,我创建了创建Excel文件的线程。这个函数也会在完成后编辑隐藏字段的值,这是因为当线程启动时我切换到多视图控件中的另一个视图并使用timmer我检查隐藏字段的值何时更改为我知道文件已完成。

Protected Sub ButtonExcel2003_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ButtonExcel2003.Click
'This function saves a config file
SaveConfigFile()
'Here I check if the file was created
If File.Exists(Server.MapPath("./Downloads/Configs.txt")) Then
        Try
            'FillExcel creates the excel file
            Dim t As New Thread(New ThreadStart(AddressOf Me.FillExcel))
            t.Start()
            'I have a Multiview Control, I want that while the excel file is being created show the view number 2 that says "creating"
            Me.MultiView.ActiveViewIndex = 2
        Catch ex As Exception
            Me.Label2.Text = ex.Message
        End Try
    Else
        MsgBox1.ShowMessage("Couldn't create the file")
    End If
End Sub

这是FillExcel方法

Private Sub FillExcel()
    _IsProcessing = True

    Dim Celdas(13) As String
    Celdas(0) = "A"
    Celdas(1) = "B"
    Celdas(2) = "C"
    Celdas(3) = "D"
    Celdas(4) = "E"
    Celdas(5) = "F"
    Celdas(6) = "G"
    Celdas(7) = "H"
    Celdas(8) = "I"
    Celdas(9) = "J"
    Celdas(10) = "K"
    Celdas(11) = "L"
    Celdas(12) = "M"

    Try
        If (File.Exists(Me.sourcefile)) Then
            File.Delete(Me.sourcefile)
        End If
        'Template Name
        File.Copy(Me.CopySourceFile, Me.sourcefile)
    Catch ex As Exception
        Me._FinExcel = -1
        Me._IsProcessing = False
        Exit Sub
    End Try

    Dim strConex As String = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
                        "Data Source=" & Me.sourcefile & ";" & _
                        "Extended Properties='Excel 8.0;HDR=NO;ReadOnly=0;'"
    Dim conexion As New OleDbConnection(strConex)
    Dim cmd2 As New OleDbCommand("", conexion)

    Try
        If (conexion.State = ConnectionState.Closed) Then conexion.Open()

        If Me.IndexTipo = 0 And Me.Rows > 0 Then
            tipo = "SI"
        End If

        Dim dt As DataTable = getData(tipo, Periodo, 0, -1)

        If dt.Rows.Count > 0 Then
            If dt.Rows.Count <= 65536 Then
                'Is 2003 version
                Dim cols = dt.Columns.Count - 1
                Dim i = 1
                Dim r As DataRow
                If i = 1 Then
                    For j As Integer = 0 To cols
                        cmd2.CommandText = "UPDATE [Hoja1$" & Celdas(j) & (i).ToString & ":" & Celdas(j) & (i).ToString & "] SET F1='" & gridviewens.HeaderRow.Cells(j).Text & "'"
                        cmd2.ExecuteNonQuery()
                    Next
                End If
                For Each r In dt.Rows
                    i += 1
                    For j As Integer = 0 To cols
                        cmd2.CommandText = "UPDATE [Hoja1$" & Celdas(j) & (i).ToString & ":" & Celdas(j) & (i).ToString & "] SET F1='" + r(j).ToString & "'"
                        cmd2.ExecuteNonQuery()
                    Next
                Next
                Me._FinExcel = 1
                Me._IsProcessing = False
            ElseIf dt.Rows.Count > 65536 Then
                If (conexion.State = ConnectionState.Open) Then conexion.Close()
                Me._FinExcel = 2
                Me._IsProcessing = False
            End If
        Else
            'No data to generate
            If (conexion.State = ConnectionState.Open) Then conexion.Close()
            Me._FinExcel = 0
            Me._IsProcessing = False
        End If

    Catch ex As Exception
        If (conexion.State = ConnectionState.Open) Then conexion.Close()
        Me._FinExcel = -1
        Me._IsProcessing = False
    End Try
    If (conexion.State = ConnectionState.Open) Then conexion.Close()

    _IsProcessing = False
End Sub

1 个答案:

答案 0 :(得分:0)

如果从ASP.NET页面启动线程,则必须在分派页面之前完成。在页面启动的所有线程终止之前,不会调度该页面。在调度页面之前,asp.net页面等待所有线程加入。因此,在这种情况下,您无法从单独的线程中获得任何收益。

然而,asp.net的好处是我们有办法持久化对象。例如会话,应用程序,查看状态等。

在这种情况下,我通常会创建一个对象并将其保存在会话变量中。我让对象完成所有工作,并通过UpdatePanel等(或任何其他方法)跟踪页面上的进度。

这是如何解决它的骨架。我相信你可以根据自己的需要调整这个解决方案。您需要自己放置MultiView,错误处理等。为了保持答案,我在这里省略了它。

  1. 在aspx页面上放置ScriptManager,Timer和UpdatePanel。在UpdatePanel中添加标签和按钮以跟踪进度。您可以稍后根据需要自定义UpdatePanel的外观。

    <%@ Page Language="vb" AutoEventWireup="false" CodeBehind="WebForm1.aspx.vb" Inherits="WebApplication1.WebForm1" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
            <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
            <asp:Timer ID="Timer1" runat="server" Enabled="false" Interval="1000" ></asp:Timer>
            <div>
                <h1>Long Running Task Demo</h1>
                <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                    <ContentTemplate>
                        <asp:Label runat="server" ID="ProgressLabel" Text="Click the Button to write Excel file." />
                        <asp:Button ID="Button1" runat="server" Text="Button" />
                    </ContentTemplate>
                    <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
                    </Triggers>
                </asp:UpdatePanel>
            </div>
        </form>
    </body>
    </html>
    
  2. 打开代码隐藏文件并添加以下代码。 (请注意,我已将FillExcel方法移到另一个类中。)

    Public Class WebForm1
        Inherits System.Web.UI.Page
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            ' create an object of our WriteExcelFileAsync class
            Dim AsyncExcelFileWriter As New WriteExcelFileAsync(Server.MapPath("./Downloads/Configs.txt"))
            AsyncExcelFileWriter.DoWork()
    
            ' save the object in session variable so that it can be retrieved back
            Session("AsyncExcelFileWriter") = AsyncExcelFileWriter
    
            ' enable the timer
            Timer1.Enabled = True
        End Sub
    
        Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
            ' retrieve the WriteExcelFileAsync object back from session variable
            Dim AsyncExcelFileWriter As WriteExcelFileAsync = Session("AsyncExcelFileWriter")
    
            If AsyncExcelFileWriter IsNot Nothing AndAlso AsyncExcelFileWriter.IsProcessing Then
                ' if it has not finished doing work (writing file) yet, show progress message..
                ' customize this as per your needs
                ProgressLabel.Text = "Writing file... Please wait...blah blah blah..."
            Else
                ' if the work is complete, show appropriate message
                ProgressLabel.Text = "Finished writing file."
                ' disable the timer
                Timer1.Enabled = False
                ' remove the object from memory and session variable
                If AsyncExcelFileWriter IsNot Nothing Then
                    AsyncExcelFileWriter = Nothing
                    Session.Remove("AsyncExcelFileWriter")
                End If
            End If
        End Sub
    End Class
    
    Public Class WriteExcelFileAsync
        Private SourceFile As String
    
        Private _IsProcessing As Boolean
        Public ReadOnly Property IsProcessing As Boolean
            Get
                Return _IsProcessing
            End Get
        End Property
    
        Public Sub New(sourceFile As String)
            Me.SourceFile = sourceFile
        End Sub
    
        Public Sub DoWork()
            Dim th As New Threading.Thread(AddressOf DoWorkTh)
            th.Start()
        End Sub
    
        Private Sub DoWorkTh()
            _IsProcessing = True
            FillExcel()
            _IsProcessing = False
        End Sub
    
        Private Sub FillExcel()
    
            'your FillExcel method...
            'write your file here
    
        End Sub
    
    End Class