Joh*_*nny 1 c# user-interface backgroundworker
我有以下代码,你可以看到后台工作程序搜索文件,并且在进度中更改了事件文件被添加到listview,但是由于有很多文件被添加到listview,UI变得没有响应,我可以睡在循环中的线程,但我不认为这是一个很好的做法,什么是防止UI冻结的最佳方法?
为了详细说明,listview是表单上的表单控件.
void bg_DoWork(object sender, DoWorkEventArgs e)
{
Stack<string> dirs = new Stack<string>(20);
dirs.Push(e.Argument.ToString());
while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try { subDirs = System.IO.Directory.GetDirectories(currentDir); }
catch (UnauthorizedAccessException) { continue; }
catch (System.IO.DirectoryNotFoundException) { continue; }
string[] files = null;
try { files = System.IO.Directory.GetFiles(currentDir); }
catch (UnauthorizedAccessException) { continue; }
catch (System.IO.DirectoryNotFoundException) { continue; }
foreach (var file in files) { bg.ReportProgress(0, file); }
foreach (string str in subDirs) { dirs.Push(str); }
}
}
void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listView1.Items.Add(e.UserState.ToString());
}
Run Code Online (Sandbox Code Playgroud)
所以这里的问题ReportProgress实际上是异步的.在继续继续工作之前,它不会等待相应的UI更新实际生成.通常情况下这很好.在大多数情况下,没有令人信服的理由放慢你的生产工作只是为了等待UI更新.
但有一个例外.如果您ReportProgress经常调用它实际上没有时间在添加下一个进度更新之前完成先前的进度更新,那么最终发生的事情是您填写消息队列以请求更新进度.你有这么多文件,获取这些文件列表只需要很少的时间.它实际上比编组UI线程和更新UI花费的时间要少得多.
由于此队列最终被备份,因此任何其他UI更新都需要在它们可以执行任何操作之前通过该长队列.
对更新进行批处理并更少地指出进度是一种可能的解决方案.根据您的情况,它可能会也可能不会被接受.它几乎肯定会有所帮助,但取决于根据您正在做的事情以及生成数据的速度来更新UI需要多长时间,甚至可能会导致问题.如果它适用于您的特定情况,那很好.
另一个选项是更改更新进度的方式,以便您的工作人员在继续之前等待UI更新.显然,除非你需要这样做,否则你应该避免这种情况,因为这意味着当你在工作时没有冻结用户界面时,你的工作会花费更长的时间.虽然有很多方法可以做到这一点,但最简单的方法是使用Invoke(不 BeginInvoke):
foreach (var file in files)
listView1.Invoke(new Action(()=>listView1.Items.Add(file));
Run Code Online (Sandbox Code Playgroud)
虽然Invoke从a 调用BackgroundWorker通常是代码味道,并且应该避免,但这是一种特殊情况.
请注意,即使你做最终诉诸使用Invoke这里我还是建议分批这样的调用已增加超过每调用只有一个项目的更多.如果单个目录中的文件数量足够低,则将整个文件foreach放入其中Invoke,如果您的子目录往往只有很少的文件(即,它们非常深,不宽),请考虑将所有文件放入临时列表,直到它足够大,值得批量进入Invoke.根据您的数据使用不同的方法,看看什么效果最好.
| 归档时间: |
|
| 查看次数: |
6543 次 |
| 最近记录: |