当迭代一个大列表时,是否有更快的方法来更新UI ?

本文关键字:方法 UI 更新 是否 迭代 一个 列表 | 更新日期: 2023-09-27 18:06:54

我对一个大约有35万个条目的字典做了三个测试。我需要知道在30秒内我可以迭代多少项。

我最初的测试只是在没有检查和UI/控制台更新的情况下循环遍历字典。它在开始测试后仅一秒钟就完成了。

第二个测试是将计数写入控制台。约23600件物品。

foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles)
{
    fileCount++;
    Console.WriteLine(fileCount.ToString());
}
然后,我测试了使用计数更新表单时它的运行速度。它达到了16000多件。
foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles)
{
    fileCount++;
    MessageReceived(this, GetMessageReceivedEventArgs("Proactive Checks - RIFT",
    string.Format("Analyzing Drive {0} - MFT Record {1} of {2}", drive.Name, fileCount.ToString(), mftFiles.Count.ToString()), string.Empty));
}

在循环中执行任何类型的条件逻辑都会大大降低循环的速度。我最初在循环外创建了一个秒表,并在循环内检查它,看看它何时到达某个时间。一分钟过去了,它只迭代了大约5000个条目。

有一个c++应用程序做了和我正在做的类似的事情。它只需要不到60秒的时间就可以遍历所有300,000个条目,更新UI,找到所有重复项,并为每个重复项创建哈希值。

有没有更好的方法来遍历字典?

当迭代一个大列表时,是否有更快的方法来更新UI ?

您已经使用更快的方法遍历字典了。没有其他更快的方法来遍历字典了。参见在c#中迭代字典的最佳方法是什么?

如果你想在达到一定时间后停止,那么这可能会起作用

var t = new Thread(() => {
   foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles)
   {
       fileCount++;
       Console.WriteLine(fileCount.ToString());
   }
});
t.Start();
// Sleep the current thread as long as you want to run the task
Thread.Sleep(/* Specify time in milliseconds */);
// After that abort the Thread to exit the job
t.Abort();

进一步使用StringBuilder代替String。格式化,因为它必须解析字符串,这需要时间。

编辑为了快速更新UI,在另一个线程中运行MFT扫描,在第二个线程中运行Watch扫描。不是在每个文件上更新UI,而是在特定时间后更新。我通过使用下面的代码在更新UI上取得了很好的性能。

public MyForm() {
    InitializeComponent();
    scanProgress = new Action(() => {
        MessageReceived(this, GetMessageReceivedEventArgs("Proactive Checks - RIFT", string.Format("Analyzing Drive {0} - MFT Record {1} of {2}", drive.Name, cMft, count, string.Empty));
    });
}
Action<> scanProgress;
int cMft = 0;
int count;
void ScanMft() {
    count = mftFiles.Count;
    foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles) {
        cMft++;
        /* Scan MFT */
    }
}
void WatchScan() {
    while (cMft < count) {
       Thread.Sleep(200); 
       this.BeginInvoke(scanProgress);
    }
}
void RunScan() {
    new Thread(ScanMft).Start();
    new Thread(WatchScan).Start();
}

有两个问题,如何做得更快(你没有提供足够的代码,你的瓶颈绝对不应该迭代集合本身,你得到的那些数字是痛苦的慢,你在调试测试吗?永远不要在调试中进行性能测试!)

发布更多的代码并在发布中进行测试,不要发布示例测试,而是发布您的实际代码,我们可以帮助您。

问题的第二部分是,如何在处理时避免阻塞UI,这很简单,不要在主线程上处理,将整个函数移动到另一个线程并定期(每100分钟?(插入)通过使用control更新控件。BeginInvoke http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke (v = vs.110) . aspx

这意味着UI在你的处理过程中是完全响应的,只有在它实际被更新的时候才会冻结一小段时间,所以它会一直响应。

如果所有这些还不够,另一个优化是,不要做你不需要做的工作!坚持我所说的(在另一个线程上处理),但不要更新UI,除了前200或300个元素,然后实现一个虚拟化控件,只显示当前应该看到的内容(在WPF中做得微不足道,但我假设你必须在winform中滚动自己的组件)。请注意,如果你在项目的早期,我强烈建议你切换到WPF,因为你的应用程序听起来像是可以使用它(不是一个微不足道的商业应用程序与几个按钮/标签)

编辑:只是为了清楚,枚举字典不可能是你的瓶颈,我刚刚在我的中程机器上进行了测试,我在字典中枚举了一百万个键值对。12毫秒!

编辑2:然而控制台。WriteLine肯定会成为您的瓶颈,这就是为什么您不应该在测试时引入不相关的api,迭代到下一项= no work,构建字符串= a little work,实际将其传递到控制台进行打印= lots of work

你已经回答了自己的问题。你已经证明了你可以在不到一秒的时间内遍历字典,所以听起来你不需要增加速度——慢与字典无关。它是messagerreceived函数调用。我不知道这个函数是做什么的,所以我不能排除为什么它很慢。

我也会尝试删除这个:

mftFiles.Count.ToString()

您可以在循环之外执行此操作并存储该值,这可能会有所帮助。

您可以创建一个工作线程并在其中放置foreach循环。通过这样做,UI和foreach循环将在不同的线程上运行,所以你不需要更新UI。

另一种替代方法(我不推荐,因为它有自己的后果)是将

Application.DoEvent();

在foreach循环。这样,您的UI将在每次迭代中得到更新。