Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
' ** FIRST ATTEMPT
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** FIRST ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 2:12.07
' ** Lines Written: 65358
' ** File Size: 8,166 KB
' ** SECOND ATTEMPT
stopWatch.Start()
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
TestOutputFile.Write(.ReadToEndAsync.Result)
End Using
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
' ** SECOND ATTEMPT
' ** Records Retrieved: 65358
' ** Time To Complete: 1:04.01
' ** Lines Written: 65358
' ** File Size: 8,102 KB
End With
End Using
我对每种方法都进行了多次测试,得出了几乎相同的结果。
第一次尝试
第二次尝试
UpdateForm.UpdateProgress
使用的方法
(用于保持表单响应并显示导出的当前进度)将导致流程花费更长的时间,因为表单更新和其他相关操作,更不用说逐行写入文件了。这就是为什么我希望通过在一行代码中进行完全转储来减少额外调用的数量。问题是,如果我使用“一行程序”,在流程完成之前,表单完全没有响应。
我已经试着把代码从
第二次尝试
Async
方法,但我
通常不熟悉异步方法,所以我(显然)做得不对:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
' ** METHOD 3
Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
End Using
' ** METHOD 3 RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:15.07
' ** Lines Written: 34
' ** File Size: 4 KB
End Sub
还有一件事要提:我试着把所有这些都搬到一个
BackgroundWorker
UpdateForm.UpdateProgress
使申请完全跳过实际倾销过程的方法。目前,我已经放弃了尝试将其放到另一个线程上,但我仍然愿意接受其他建议。这实际上是我正在丢弃的一张较小的桌子,所以我并不期待其中一张较大的桌子能做什么。
UpdateForm
类,该类已在库中实现,以便跨其他应用程序重用:
Imports System.Windows.Forms
Namespace Common
Public Class FormHandler
Implements IDisposable
Public Property ApplicationForm As Form
Public Property ApplicationStatusLabel As Label
Public Property ApplicationToolStripLabel As ToolStripStatusLabel
Public Property ApplicationProgressBar As ProgressBar
Private LabelVisibleState As Boolean = True
Private ProgressBarVisibleState As Boolean = True
Private CurrentStatusText As String
Private CurrentProgress As Integer
Public Sub New(ByVal AppForm As Form)
ApplicationForm = AppForm
End Sub
Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
ApplicationForm = ApplicationProgressBar.Parent.FindForm
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationStatusLabel = StatusLabel
ApplicationToolStripLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationStatusLabel
.Visible = True
.Text = ""
End With
End Sub
Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
ApplicationForm = AppForm
ApplicationToolStripLabel = StatusLabel
ApplicationStatusLabel = Nothing
ApplicationProgressBar = Progress
LabelVisibleState = StatusLabel.Visible
ProgressBarVisibleState = Progress.Visible
With ApplicationProgressBar
.Minimum = 0
.Maximum = 100
.Value = 0
.Visible = True
End With
With ApplicationToolStripLabel
.Visible = True
.Text = ""
End With
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
CurrentStatusText = StatusText
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal StatusText As String)
CurrentStatusText = StatusText
CurrentProgress = 0
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
CurrentProgress = Convert.ToInt32(PercentComplete)
UpdateStatus()
End Sub
Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
UpdateStatus()
End Sub
Friend Sub ResetProgressUpdate()
CurrentStatusText = ""
CurrentProgress = 0
UpdateStatus()
End Sub
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress(CurrentStatusText)
End If
End If
End Sub
Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not ProgressText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Text = ProgressText
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Text = ProgressText
End If
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Value = CurrentProgress
End If
End With
ApplicationForm.Refresh()
Application.DoEvents()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not ApplicationForm Is Nothing Then
ApplicationForm.Dispose()
End If
If Not ApplicationStatusLabel Is Nothing Then
ApplicationStatusLabel.Visible = LabelVisibleState
ApplicationStatusLabel.Dispose()
End If
If Not ApplicationToolStripLabel Is Nothing Then
ApplicationToolStripLabel.Visible = LabelVisibleState
ApplicationToolStripLabel.Dispose()
End If
If Not ApplicationProgressBar Is Nothing Then
ApplicationProgressBar.Visible = ProgressBarVisibleState
ApplicationProgressBar.Dispose()
End If
End Sub
End Class
End Namespace
编辑
根据@the_lotus评论中的建议,我修改了我的
第一次尝试
稍微检查一下当前进度的值(我声明了
CurrentProgress
变量作为
Integer
戏剧性地
改进了所需的时间:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
With COPYReader
Dim stopWatch As New Stopwatch
Dim ts As TimeSpan
Dim elapsedTime As String
Dim CurrentProgress As Integer = 0
stopWatch.Start()
Dim BufferText As String = .ReadLine
Do While Not BufferText Is Nothing
CurrentPosition += 1
OutputFile.WriteLine(BufferText)
' ** Checks to see if the value of the ProgressBar will actually
' ** be changed by the CurrentPosition before making a call to
' ** UpdateProgress. If the value doesn't change, don't waste
' ** the call
If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
If Not UpdateForm Is Nothing Then
UpdateForm.UpdateProgress(CurrentProgress)
End If
End If
BufferText = .ReadLine
Loop
OutputFile.Flush()
OutputFile.Close()
stopWatch.Stop()
ts = stopWatch.Elapsed
elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
编辑#2
读:“复制/粘贴”
UpdateProgress
方法,我已经将更改值的测试移到了那里,它似乎在以相同的性能改进运行。同样,为了完整起见,下面是执行实际进度/状态更新所涉及的两个私有方法的代码:
Private Sub UpdateStatus()
If Not ApplicationForm Is Nothing Then
If ApplicationForm.InvokeRequired Then
Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
Try
ApplicationForm.Invoke(UpdateInvoker)
Catch ex As Exception
Dim InvokeError As New ErrorHandler(ex)
InvokeError.LogException()
End Try
Else
UpdateApplicationProgress()
End If
End If
End Sub
Private Sub UpdateApplicationProgress()
Dim Changed As Boolean = False
If Not ApplicationForm Is Nothing Then
With ApplicationForm
If Not CurrentStatusText Is Nothing Then
If Not ApplicationStatusLabel Is Nothing Then
If ApplicationStatusLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationStatusLabel.Text = CurrentStatusText
End If
End If
If Not ApplicationToolStripLabel Is Nothing Then
If ApplicationToolStripLabel.Text <> CurrentStatusText Then
Changed = True
ApplicationToolStripLabel.Text = CurrentStatusText
End If
End If
End If
If Not ApplicationProgressBar Is Nothing Then
If ApplicationProgressBar.Value <> CurrentProgress Then
Changed = True
ApplicationProgressBar.Value = CurrentProgress
End If
End If
End With
If Changed Then
ApplicationForm.Refresh()
End If
Application.DoEvents()
End If
End Sub
这样做还有一个额外的好处,就是将一些以前丢失的响应返回到表单。我希望这些代码和信息中至少有一些对其他人有帮助。