Vb.net 如何加快VB复制速度

Vb.net 如何加快VB复制速度,vb.net,Vb.net,当项目数较低时,我正在成功运行以下循环。然而,当在ListView上运行一个较大的列表时,似乎花费的时间太长了。我用8700个文件的列表进行了测试,大约花了两个小时才完成。我能做些什么来加快速度吗?我想删除取消按钮的复选框会有所帮助,但为了可用性,我想保留它。正如我在之前的文章中提到的,我对VisualBasic非常陌生,所以请提供大量的解释和建议。谢谢代码如下: For i As Integer = 0 To m_CountTo ' Has the backgroun

当项目数较低时,我正在成功运行以下循环。然而,当在ListView上运行一个较大的列表时,似乎花费的时间太长了。我用8700个文件的列表进行了测试,大约花了两个小时才完成。我能做些什么来加快速度吗?我想删除取消按钮的复选框会有所帮助,但为了可用性,我想保留它。正如我在之前的文章中提到的,我对VisualBasic非常陌生,所以请提供大量的解释和建议。谢谢代码如下:

    For i As Integer = 0 To m_CountTo

        ' Has the background worker be told to stop?
        If BackgroundWorker1.CancellationPending Then
            ' Set Cancel to True
            e.Cancel = True
            Exit For
        End If

        'Select the row from the LVFiles ListView, then move the first column (0) into strSourceFilePath and the last
        ' column (3) into strDestFilePath. Execute the CopyFile method to copy the file.

        LVFiles.Items(i).Selected = True
        strSourceFilePath = LVFiles.SelectedItems(i).SubItems(0).Text
        strDestFilePath = LVFiles.SelectedItems(i).SubItems(3).Text
        My.Computer.FileSystem.CopyFile(strSourceFilePath, strDestFilePath, overwrite:=False)

        ' Report The progress of the Background Worker.
        BackgroundWorker1.ReportProgress(CInt((i / m_CountTo) * 100))
        ' Me.LabelStatus.Text = FormatPercent((i + 1) / (intLVIndex + 1), 2) ' Show Percentage in Label
        SetLabelText_ThreadSafe(Me.LabelStatus, FormatPercent(i / m_CountTo, 2))

    Next

Backgroundworker封装了一个新线程。您不能直接访问在另一个线程中创建的控件。如果您这样做,您将由于跨线程操作而获得一个
InvalidOperationException
。但是,Backgroundworker提供了一些在线程之间共享数据(或访问控件)的功能。你应该使用它们

    Private Sub StartBGW_Click(sender As Object, e As EventArgs) Handles StartBGW.Click
        Dim dict As New Dictionary(Of String, String)
        For i As Integer = 0 To m_CountTo
            dict.Add(Me.LVFiles.Items(i).SubItems(0).Text,
                     Me.LVFiles.Items(i).SubItems(3).Text)
        Next

        Me.BackgroundWorker1.RunWorkerAsync(dict)
    End Sub
首先,我们准备一个字典,其中包含源代码为
,目标代码为
。此对象作为参数提供给
BackgroundWorker

现在是关键部分:

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim counter As Integer = -1
        Dim dict = DirectCast(e.Argument, Dictionary(Of String, String))
        For Each kvp In dict
            counter += 1

            ' Has the background worker be told to stop?
            If Me.BackgroundWorker1.CancellationPending Then
                ' Set Cancel to True
                e.Cancel = True
                Exit For
            End If

           'Select the row from the LVFiles ListView, then move the first column (0) into strSourceFilePath and the last
           ' column (3) into strDestFilePath. Execute the CopyFile method to copy the file.

            My.Computer.FileSystem.CopyFile(kvp.Key, kvp.Value, overwrite:=False)

            ' Report The progress of the Background Worker.
            Me.BackgroundWorker1.ReportProgress(CInt((counter / m_CountTo) * 100), counter)
        Next
    End Sub
我们不再访问
列表视图
。相反,我们使用通过
e.Argument
提供给我们的字典作为参数。
BackgroundWorker1.ReportsProgress
行中也有一点不同。我还使用了第二个参数将当前索引传递给
ProgressChanged
事件,该事件可以通过
e.UserState
获得

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Me.LVFiles.Items(Convert.ToInt32(e.UserState)).Selected = True
        Me.LabelStatus.Text = e.ProgressPercentage.ToString
    End Sub
此事件旨在通过调用线程的SynchronizationContext引发,在本例中为UI线程。在这里,我们可以安全地访问任何控件并更新它们。索引作为
e.UserState
传递,因此我们可以访问相关项并将其
Selected
属性设置为true


最大的改进来自将
Me.LVFiles.SelectedItems(i).SubItems(0.Text
更改为
Me.LVFiles.Items(i).SubItems(0.Text
)。我不是专业人士,但似乎
SelectedItems
不是真正的列表。相反,它使用SendMessageAPI迭代每个项目,直到达到所需的索引。这就是为什么索引越高所需的时间越长。每次它都从第一项开始,并在其中迭代。很多手术

第二个改进是分离访问UI控件的代码。现在所有这些都用一种方法完成了。更清晰易读


更新:@Enigmativity提到
SelectedListViewItemCollection
实现了
IList
,因此是一个真正的列表。即使它没有包含所有选定项的基础列表,就像您在
ListViewItemCollection
中所做的那样。我的意思是,访问单个元素更复杂。

删除取消测试不会显著提高性能。如果使用Windows资源管理器(拖放、复制粘贴等),复制8700个文件需要多长时间?如果时间大致相同,则性能问题不在程序中。如果使用CopyFile注释掉该行,需要多长时间?假设发布的代码正在
BackgroundWorker1.DoWork
事件处理程序中运行,如何不获取System.InvalidOperationException:“跨线程操作无效:从创建它的线程以外的线程访问控制“LVFiles”。错误?如果删除
LVFiles.Items(i),还可以加快过程的速度.Selected=True
更新UI会消耗更多的系统资源,尤其是当它有一个大数据标记时-如果我进行拖放操作,大约需要15分钟。Andrew Morton-如果我注释掉CopyFile行并运行它,大约需要1小时15分钟。我想知道这是否是问题的一部分。我使用了一个脏补丁,关闭了非法跨线程调用的检查。我不知道错误是否仍然生成,然后被忽略,或者是什么。我知道我可能应该找一位代表或其他什么人,但经过一些调查,这似乎超出了我目前的才能水平。Mo Khalefa-我试试这个。达曼-谢谢你的清晰解释。这使它成为我将来可以参考的东西。一个问题是,进度状态根本没有改变,即使在查看代码时,它似乎应该改变。此外,它还将它从一个大型测试目录上的2个小时加速到了半个小时,看起来仍然很长。你有没有看到其他可能加快速度的东西?我注释掉了更新ListView的那一行,它将每一行显示为选中的,所以不是这样。拖放时间为12分钟,因此它也不仅仅是一台速度慢的笔记本电脑。我越来越绝望了。达曼-当我说进度没有更新时,发生的是它在开始时被设置为0,然后一直保持到完成,然后切换到100。达曼-我的错误。有一个打字错误阻止了进度的更新。它现在运行良好,但仍然非常慢。@RossfromBrooklin我已经尝试了30000个文件(总大小约为1GB)。Windows资源管理器大约需要50秒,我的代码需要70秒。由于.NET是一个托管框架,因此它的代码速度较慢也就不足为奇了。目前我不知道如何进一步改进。凯科-谢谢你的数字。我不知道发生了什么事情,它太慢了。我注意到,它在大约一分钟半的时间内完成了前10%(800个文件),然后每105个文件都花费了越来越长的时间。这是比我聪明的人的线索吗?(这不是一个很难满足的资格要求)我只尝试每50条记录更新一次进度,但没有任何明显的区别。