在迭代大型列表时,是否有更快的方法来更新UI?

时间:2014-06-06 03:24:24

标签: c# winforms loops dictionary

我对一个有大约35万件物品的词典进行了三次测试。我需要看看我可以在30秒的时间内迭代多少项目。

我的初始测试只是在字典中循环,没有检查,也没有UI /控制台更新。它在开始测试后仅仅一秒就完成了。

第二项测试是将计数写入控制台。已达到约23,600件物品。

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,查找所有重复项,并为每个副本创建哈希值。

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

4 个答案:

答案 0 :(得分:0)

您已经在使用更快的方式来遍历字典。没有其他更快捷的方法来遍历字典。见What is the best way to iterate over a Dictionary in 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.Format,因为它必须解析需要时间的字符串。

修改 快速更新UI在其他线程中运行MFT扫描并在第二个线程中监视扫描。而不是更新每个文件上的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();
}

答案 1 :(得分:0)

那里有两个问题,如何更快地完成它(你没有提供足够的代码,你的瓶颈肯定不应该自己迭代一个集合,而你得到的那些数字是非常缓慢的,是你在调试中测试了吗?从来没有在调试中进行过性能测试!)

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

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

更新控件

这意味着用户界面在处理过程中处于完全反应状态,只有在实际更新时才会对微小时间片进行频繁处理,以便始终保持响应。

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

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

编辑2:但是Console.WriteLine肯定是你的瓶颈,这就是为什么你不应该在测试时引入不相关的API,迭代到下一个项目=没有工作,构建字符串=一点工作,实际传递它到控制台进行打印=大量工作

答案 2 :(得分:-1)

您已经回答了自己的问题。你已经证明你可以在一秒钟内迭代字典,所以听起来你不需要提高速度 - 慢速与字典无关。这是MessageReceived函数调用。我不知道那个功能是做什么的,所以我无法解决为什么它很慢。

我也会尝试删除它:

mftFiles.Count.ToString()

你可以在循环外执行一次并存储值,这可能会有所帮助。

答案 3 :(得分:-1)

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

另一种替代方式(我不建议,因为它有其自身的后果)是放

Application.DoEvent();
在foreach循环中

。这样做,您的UI将在每次迭代时得到更新。